函数作为模板haskell引用中使用的参数

jek*_*jek 5 haskell template-haskell

这部分是一个函数的Lift实例的后续操作.但是,答案是全局定义函数或在引号内重写它.但是,我们将在fooa f的范围内使用很多具有不同功能的let.这使得我们几乎不可能定义f的多个全局版本.后者在引用中编写函数的解决方案似乎等同于在函数上编写一个提升.

那么,有没有办法解除在Haskell模板引用中使用的函数作为参数?


一个非常人为的例子:

foo.hs

{-# Language TemplateHaskell #-}
import Language.Haskell.TH

foo :: (Int->Int) -> Int -> ExpQ
foo f x = [|f x|]

g :: ExpQ
g = 
    let 
        f = (\x -> x+1)
        f' = (\x' -> f(x') + 1)
    in foo f' 0
Run Code Online (Sandbox Code Playgroud)

将失败:

foo.hs:5:11:
    No instance for (Language.Haskell.TH.Syntax.Lift (Int -> Int))
      arising from a use of ‘Language.Haskell.TH.Syntax.lift’
    In the expression: Language.Haskell.TH.Syntax.lift f
    In the expression:
      [| f x |]
      pending(rn) [x, f]
    In an equation for ‘foo’:
        foo f x
          = [| f x |]
            pending(rn) [x, f]
Run Code Online (Sandbox Code Playgroud)

ben*_*ofs 4

无法实现提升功能。然而,有两种可能适合您的替代方案:

只提升函数的结果,在编译时应用该函数

在您的特殊情况下,因为您在编译时知道和,x所以f您可以f x在编译时计算并仅拼接结果:

foo :: (Int->Int) -> Int -> ExpQ
foo f x  = [| $(lift $ f x) |]
--       = lift $ f x
-- foo f = lift . f
Run Code Online (Sandbox Code Playgroud)

这不会更改 的类型签名f,但它要求您知道要提供给 的所有参数f。您需要导入Language.Haskell.TH.Syntaxlift函数。

将函数的表达式作为参数传递

如果您无法使用第一个解决方案,还有另一种选择。现在,您不再传递函数,而是传递函数的拼接作为参数:

foo :: ExpQ -> Int -> ExpQ
foo f x = [| $f x |]
Run Code Online (Sandbox Code Playgroud)

有两个缺点:首先,您失去了类型安全性,因为没有检查拼接是否真的扩展到可以应用于Int. 并且您需要更改调用代码,如下所示:

g :: ExpQ
g = 
    let 
        f =  [| \x -> x+1 |]
        f' = [| \x' -> $f x' + 1 |]
    in foo f' 0
Run Code Online (Sandbox Code Playgroud)