Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lift instance for a function?

Tags:

haskell

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

[|f|]
Some functions seem to work automatically. 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.

like image 341
Greg Weber Avatar asked Mar 14 '11 04:03

Greg Weber


People also ask

What is lifting a function?

Lifting is a concept which allows you to transform a function into a corresponding function within another (usually more general) setting.

What is lift in functional programming?

An individual "lift" transforms a local function into a global function. It is a two step process, consisting of; Eliminating free variables in the function by adding parameters. Moving functions from a restricted scope to broader or global scope.


1 Answers

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 |]
like image 83
intoverflow Avatar answered Oct 20 '22 02:10

intoverflow