我正在尝试为模板haskell中的函数创建类型签名。这样做有简单的方法吗?
我在此期间做了一些解决方法,但它应该更容易,对吧?
-- TH.hs
module Lib.TH (mkFunction) where
import Language.Haskell.TH
mkFunction n = do
let name = mkName n
[d|
$( ... ) :: Integer -> Integer
$(varP name) = \x -> x + 2|]
-- Other.hs
import TH
mkFunction "test"
我应该在上面$( ... )
写些什么?我尝试过的所有内容都会产生
Invalid type signature: ... :: Integer -> Integer
Should be of form <variable> :: <type>
答案 0 :(得分:4)
我不是专家,但我通过在文档中挖掘并遵循类型错误找到了一种方法。
import Language.Haskell.TH
import Control.Applicative ((<$>))
mkFunction n = do
let name = mkName n
[d|
$( return . SigD name <$> [t| Integer -> Integer |] )
$(varP name) = \x -> x + 2 |]
我不知道是否有更清洁的方式。
注意这适用于7.8.3但不适用于7.10.2。 : - (
答案 1 :(得分:1)
我不认为(猜测)有一种方法可以在模板Haskell类型签名中拼接名称(对于GHC≤7.10)。要仍然使用准引用,您可以尝试在之后处理AST以在适当的位置设置名称(并保持其余部分不变):
setName :: Name -> DecsQ -> DecsQ
setName = fmap . map . sn
where sn n (SigD _ t) = SigD n t
sn n (ValD (VarP _) x y) = ValD (VarP n) x y
sn _ d = d
mkFunction n = setName (mkName n)
[d|
n :: Integer -> Integer
n = \x -> x + 2 |]
这是在GHC 7.10上测试的,可能会对GHC的过去和未来版本进行微小修改。
在多大程度上,这比直接编写AST更简洁或更冗长是有争议的。它取决于您编写类似声明的频率以及引用代码的复杂性。
如果在具有多个函数(或递归函数)的声明上使用它,上面的setName
函数显然会中断。要解决这个问题,你可以用同样的精神写一个reName
函数。
答案 2 :(得分:1)
另一种解决方法。使用应用语法和重构@luqui's workaround,您将获得与所需内容接近的东西:
import Language.Haskell.TH
mkFunction n = do
let name = mkName n
(:) <$> name `sigD` [t| Integer -> Integer |]
<*> [d| $(varP name) = \x -> x + 2 |]