开发者

Lift instance for a function?

开发者 https://www.devze.com 2023-02-16 10:16 出处:网络
I need to put a function into Template Haskell code. I am using the expression syntax: [|f|] Some functions seem to work automa开发者_运维百科tically. However, for this particular one I get the follow

I need to put a function into Template Haskell code. I am using the expression syntax:

[|f|]
Some functions seem to work automa开发者_运维百科tically. However, for this particular one I get the following erroe message:

   No instance for (Lift (String -> [Content]))

I have no idea how to make a lift instance for a function, and can't seem to find any useful information. Can anyone point me to a resource or give me an idea of how this is accomplished in general? In the mean time I will see if I can pare down my specific example.


I'll take a stab, though TH can be hard to debug without seeing more code.

Let's take a look at some sample code:

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

Now we can fire this up in GHCi (I'm on version 7.0.2, which is what ships with the current Haskell Platform):

$ 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))

What I've done here is attempted to use runQ to see what the TH splice looks like for each of my functions in the sample code. It fails on baz and bez, but works for boz and byz.

Looking at the TH for boz and byz, we can see how functions are lifted: boz is basically just referring to + by name (in VarE GHC.Num.+), while byz is just referring to g by name (in VarE Main.g).

For baz and bez, this option isn't on the table: both of those functions are attempting to splice f, which is locally bound; hence, reference to VarE f wouldn't make sense outside of baz and bez.

So what's a developer to do? In short, instead of trying [| f |], you need to write the expression for f in the lift directly, in terms of identifiers that will be bound where the splice occurs.

On a side note, it is very easy to write Lift instances for algebraic data types, since you can always lift globally-defined functions. Here's one for Maybe:

instance Lift a => Lift (Maybe a) where
  lift Nothing = [| Nothing |]
  lift (Just a) = [| Just a |]
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号