模板haskell中的拼接类型签名

时间:2015-08-28 17:36:24

标签: haskell template-haskell

我正在尝试为模板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>

3 个答案:

答案 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 |]