【问题标题】:Introspecting function names in a GHC module内省 GHC 模块中的函数名称
【发布时间】:2015-03-12 11:08:01
【问题描述】:

Glasgow Haskell 编译器中是否有一种方法可以内省模块中所有函数的名称?

我正在尝试创建一个自动数据库迁移系统,给定迁移模块的名称,内省函数的名称并一次调用它们。

类似

doMigrations("Migrations.M_2015")
doMigrations("Migrations.M_2016")
-- ...

Migration.M_2015 包含在哪里

module Migration.M_2015
where

migration_2015_01_02 :: DbConnection -> Status
migration_2015_01_02 connection =
    -- ...

每个doMigration 都会在其模块中反映迁移函数的名称,并且只调用那些之前没有运行过的函数(名称保存在数据库表中)。这只会在应用程序启动时调用,因此性能不是一个大问题。反射可以在编译时或运行时发生。

【问题讨论】:

  • 有:它叫做Template Haskell。这并不容易。一些谷歌搜索应该可以让你找到它存在的少量文档。
  • 有什么理由你不能做migrationsToDo = [migration_2015_01_02, ...],然后只做map doMigrations migrationsToDo?你不需要任何形式的内省;似乎您对名称没有做任何有趣的事情,只是调用了函数。
  • 并将它们添加到已经运行的迁移表中。

标签: haskell reflection ghc


【解决方案1】:

为了做到这一点,您需要使用 GHC API——它包含在 ghc 包中(它是隐藏的)——并且文档很少。

我在这里附上一个简单的程序,它将打印出模块中导出的顶级项目的列表。这应该作为一个起点。这是一个小命令行实用程序,它有两个参数——一个模块名称和单词“class”、“data”、“function”。所以,例如:

test Prelude function

将打印模块导出的函数列表(那些不是构造函数或在类中定义的函数)。

为了编译它(假设它在test.hs),你需要这样做:

ghc -package ghc test

为了使 GHC API 包可用。

代码如下:

import Data.List ( (\\) )
import Data.Maybe (fromJust, catMaybes)
import System.Environment (getArgs)

-- the GHC API stuff

import GHC
import GHC.Paths (libdir)
import ConLike ( ConLike(..) )
import Outputable (showPpr, showSDocUnqual)
import Var (tyVarName)

showU dfs = showSDocUnqual dfs . pprParenSymName

main = do
   (mn : ty : _) <- getArgs
   a <- runGhc (Just libdir) $ do 
     dflags <- getSessionDynFlags
     setSessionDynFlags dflags
     mm <- lookupModule (mkModuleName mn) Nothing
     mi <- fmap fromJust $ getModuleInfo mm
     res <- fmap catMaybes $ mapM lookupName (modInfoExports mi)
     return $ case ty of
       "class" -> [showU dflags c' | c@(ATyCon c') <- res, isClassTyCon c']
       "data" -> [showU dflags c' | c@(ATyCon c') <- res, (not . isClassTyCon) c']
       "function" -> let cf = map getName $ concat [(classMethods . fromJust . tyConClass_maybe) c' | c@(ATyCon c') <- res, isClassTyCon c']
                         df = map getName $ concat [ tyConDataCons c' | c@(ATyCon c') <- res, (not . isClassTyCon) c']
                         ff = [ getName c | c@(AnId{}) <- res] \\ cf
                         fd = [ getName c | c@(AConLike (RealDataCon{})) <- res] \\ df
                     in [showU dflags x | x <- ff ++ fd]
       _ -> ["need to specify: class, data, or function"]
  print a

类和定义数据的列表非常简单。已定义函数列表包括在类和构造函数中定义的函数。上面的代码,对于函数,不包括这些(\\ cf\\ df)。

a 是生成的函数(或类或数据)名称列表。

调用这些函数的代码将是不同问题(和答案)的主题。

lookupModule是加载模块进行分析的函数。

getModuleInfomodInfoExports 的组合得到“stuff”列表,其中包括从模块导出的函数列表。

其余的代码是关于以可用的形式获取这些名称。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多