有没有办法使用 Template Haskell 枚举模块中的所有函数?

Posted

技术标签:

【中文标题】有没有办法使用 Template Haskell 枚举模块中的所有函数?【英文标题】:Is there a way how to enumerate all functions in a module using Template Haskell? 【发布时间】:2014-01-03 13:38:39 【问题描述】:

虽然我可以使用 reify 获取有关大多数其他语法结构的信息,但我找不到任何可以提供有关模块的信息的任何东西。

【问题讨论】:

如果整个模块(导入/导出除外)位于 TH 拼接中,则 TH 可以为您提供有关整个模块的信息。如果不是这种情况,您可以使用haskell-src-meta 来解析整个 Haskell 文件。免责声明:它不支持大多数扩展。您也可以使用Language.Haskell.TH.Quote.quoteFile,但这再次要求文件不包含导入或导出语句(这意味着它可能不是有效的 Haskell 代码)。 看看haskell-names 包。 【参考方案1】:

不幸的是,Haskell 模板目前没有这样的功能。所有解决方案都涉及解析模块的源代码。但是 TH 的 locationloc_filename 函数可以很容易地找到调用拼接的模块。

这是从我的一个项目的the source code 中提取的解决方案:

-# LANGUAGE LambdaCase, TupleSections #-
import Language.Haskell.TH
import qualified Data.Attoparsec.Text as AP
import qualified Data.Text.IO as Text
import qualified Data.Text as Text
import qualified Data.Char as Char
import Data.Maybe
import Data.List
import Control.Applicative
import Data.Traversable
import Prelude hiding (mapM)


reifyLocalFunctions :: Q [(Name, Type)]
reifyLocalFunctions =
  listTopLevelFunctionLikeNames >>=
  mapM (\name -> reifyFunction name >>= mapM (return . (name, ))) >>=
  return . catMaybes
  where
    listTopLevelFunctionLikeNames = do 
      loc <- location
      text <- runIO $ Text.readFile $ loc_filename loc
      return $ map (mkName . Text.unpack) $ nub $ parse text
      where
        parse text = 
          either (error . ("Local function name parsing failure: " ++)) id $
          AP.parseOnly parser text
          where
            parser = 
              AP.sepBy (optional topLevelFunctionP <* AP.skipWhile (not . AP.isEndOfLine)) 
                       AP.endOfLine >>=
              return . catMaybes
              where
                topLevelFunctionP = do
                  head <- AP.satisfy Char.isLower
                  tail <- many (AP.satisfy (\c -> Char.isAlphaNum c || c `elem` ['_', '\'']))
                  return $ Text.pack $ head : tail

reifyFunction :: Name -> Q (Maybe Type)
reifyFunction name = do
  tryToReify name >>= \case
    Just (VarI _ t _ _) -> return $ Just $ t
    _ -> return Nothing

tryToReify :: Name -> Q (Maybe Info)
tryToReify n = recover (return Nothing) (fmap Just $ reify n) 

【讨论】:

以上是关于有没有办法使用 Template Haskell 枚举模块中的所有函数?的主要内容,如果未能解决你的问题,请参考以下文章

在haskell中使用forall量词进行谓词?

Haskell:一个组合案例

如何从 haskell 程序调用 bash 或 shell 脚本?

Haskell导出当前模块带有额外的导入模块

C++模版编程实现Haskell的函数模式匹配特性

从Haskell中的元组中提取第n个元素(其中n和元组被赋予参数)