虽然如果不看更多代码就很难调试TH,我会采取刺激措施.
我们来看看一些示例代码:
foo.hs:
{-# Language TemplateHaskell #-}
baz x = let f y = x + y
in [| f |]
bez x = let f y = x + y
in [| \y -> f y |]
boz x = [| \y -> x + y |]
g x y = x + y
byz x = [| g x |]
Run Code Online (Sandbox Code Playgroud)
现在我们可以在GHCi中解决这个问题(我的版本是7.0.2,这是当前Haskell平台附带的版本):
$ ghci foo.hs -XTemplateHaskell
*Main> :m +Language.Haskell.TH
*Main Language.Haskell.TH> runQ (baz 2)
<interactive>:1:7:
No instance for (Language.Haskell.TH.Syntax.Lift (a0 -> a0))
arising from a use of `baz'
Possible fix:
add an instance declaration for
(Language.Haskell.TH.Syntax.Lift (a0 -> a0))
In the first argument of `runQ', namely `(baz 2)'
In the expression: runQ (baz 2)
In an equation for `it': it = runQ (baz 2)
*Main Language.Haskell.TH> runQ (bez 2)
<interactive>:1:7:
No instance for (Language.Haskell.TH.Syntax.Lift (a0 -> a0))
arising from a use of `bez'
Possible fix:
add an instance declaration for
(Language.Haskell.TH.Syntax.Lift (a0 -> a0))
In the first argument of `runQ', namely `(bez 2)'
In the expression: runQ (bez 2)
In an equation for `it': it = runQ (bez 2)
*Main Language.Haskell.TH> runQ (boz 2)
LamE [VarP y_0] (InfixE (Just (LitE (IntegerL 2))) (VarE GHC.Num.+) (Just (VarE y_0)))
*Main Language.Haskell.TH> runQ (byz 2)
AppE (VarE Main.g) (LitE (IntegerL 2))
Run Code Online (Sandbox Code Playgroud)
我在这里所做的是尝试使用runQ示例代码中的每个函数来查看TH splice的样子.它未能对baz和bez,但工程boz和byz.
看看TH for boz和byz,我们可以看到函数如何解除:boz基本上只是+通过名称(in VarE GHC.Num.+)byz引用,而只是g通过名称(in VarE Main.g)引用.
对于baz和bez,此选项不在表中:这两个函数都试图拼接f,这是本地绑定的; 因此,VarE f在baz和之外的参考是没有意义的bez.
那么开发人员应该做些什么呢?简而言之,[| f |]您需要f直接在电梯中编写表达式,而不是在绑定发生的地方绑定标识符.
另一方面,Lift为代数数据类型编写实例非常容易,因为您始终可以提升全局定义的函数.这是一个用于Maybe:
instance Lift a => Lift (Maybe a) where
lift Nothing = [| Nothing |]
lift (Just a) = [| Just a |]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
919 次 |
| 最近记录: |