我刚开始使用模板Haskell(我终于得到了一个用例,是的!)现在我认知卡住了。
我要做的是生成表单
的单例数据类型声明data $V = $V deriving (Eq,Ord)
从名称V
开始(希望以大写字符开头!)。为了明确,我正在尝试编写declareSingleton
类型的函数String -> DecsQ
(我应该在这里提到我使用的是GHC 7.6.1,模板 - haskell版本2.8.0.0)这样拼接
$(declareSingleton "Foo")
相当于
data Foo = Foo deriving (Eq,Ord)
我有以下代码正在运行并按我的意愿行事,但我对此并不满意:
declareSingleton :: String -> Q [Dec]
declareSingleton s = let n = mkName s in sequence [
dataD (cxt []) n [] [normalC n []] [''Eq,''Ord]
]
我希望得到类似以下的内容:
declareSingleton :: String -> Q [Dec]
declareSingleton s = let n = mkName s in
[d| data $n = $n deriving (Eq,Ord) |]
我尝试过,但没有用(但并非详尽无遗!),$s
,$v
,$(conT v)
,v
,'v
的各种组合所以我不得不假设我对模板Haskell如何工作的心理模型过于简单化了。
我在这里遗漏了一些明显的东西,我是否以某种必要的方式混淆了类型名称和构造函数名称,我能以一种漂亮的(r)方式编写declareSingleton
吗?
若然,怎么样;如果没有,为什么不呢?
(旁注:模板Haskell API变化很快,我很高兴 - 我希望这个简单的类型最终实现一个带有关联类型系列的多参数类型 - 但是API目前正在流失通过搜索教程并不容易!在6.12.1或7.2(当大部分现有教程编写时)中实现TH与现在的工作方式有很大差异......)
答案 0 :(得分:4)
来自Template Haskell documentation:
可以用拼接代替
- 表达;拼接表达式必须具有类型Q Exp
- 一种;拼接表达式必须具有Q Typ
类型- 顶级声明列表;拼接表达式必须具有类型Q [Dec]
所以,例如构造函数名称根本无法在当前版本的Template Haskell中拼接。
我认为你可以做很多事情来简化这个用例(除非将整个声明构建为一个字符串,并通过toDec
中的haskell-src-meta
将其转换为Dec
)。
您可以考虑简单地将声明的不同部分绑定到局部变量。虽然更冗长,但它使代码更容易阅读。
declareSingleton :: String -> Q [Dec]
declareSingleton s = return [DataD context name vars cons derives] where
context = []
name = mkName s
vars = []
cons = [NormalC name fields]
fields = []
derives = [''Eq, ''Ord]
答案 1 :(得分:0)
解析内插字符串而不是构造AST的hack:
makeU1 :: String -> Q [Dec]
makeU1 = makeSingletonDeclaration >>> parseDecs >>> fromRight >>> return
makeSingletonDeclaration :: String -> String
makeSingletonDeclaration name = [qq|data {name} = {name} deriving (Show)|]
其中:
parseDecs :: String -> Either String [Dec]
用法:
makeU1 "T"
main = print T
它需要这些包:
$ cabal install haskell-src-exts
$ cabal install haskell-src-meta
$ cabal install interpolatedstring-perl6
runnable script: