This is partially a followup to Lift instance for a function?. However, the answer there is to either globally define the function or to rewrite it inside the quotation. However, we will be using foo
a lot with different functions for f
from within the scope of a let
. This makes it just about impossible for us to define multiple global version of f. The latter solution, of writing our function within the quote, seem equivalent to writing a lift on functions.
So, is there any way of lifting functions taken as arguments to use in a template Haskell quotation?
A very contrived example:
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
Will fail with:
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]
Lifting functions is not possible. There are however two possible alternatives that might work for your:
In your special case, because you know at compile time both x
and f
, you can just compute f x
at compile time and only splice the result:
foo :: (Int->Int) -> Int -> ExpQ
foo f x = [| $(lift $ f x) |]
-- = lift $ f x
-- foo f = lift . f
This doesn't change the type signature of f
, but it requires that you know all the arguments you want to give to f
. You'll need to import Language.Haskell.TH.Syntax
for the lift
function.
If you cannot use the first solution, there is another alternative. Instead of passing the function, you now pass a splice for a function as an argument:
foo :: ExpQ -> Int -> ExpQ
foo f x = [| $f x |]
There are two disadvantages: First, you loose type-safety because it isn't checked that the splice really expands to something that can be applied to an Int
. And you need to change your calling code, like this:
g :: ExpQ
g =
let
f = [| \x -> x+1 |]
f' = [| \x' -> $f x' + 1 |]
in foo f' 0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With