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