问题
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]
回答1:
Lifting functions is not possible. There are however two possible alternatives that might work for your:
Only lift the result of the function, apply the function at compile time
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.
Pass an expression for a function as an argument
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
回答2:
The best answer for that is that passing an Expression to a function which evaluates those expressions first and then solves the outer function for example: head ( Drop 3 "Sikandar") Now here head is function and drop also the function but with an expression, so first evaluation will take place in the inner function so the result for that would be drop will drop 3 characters from there "andar" and it will pass those result to head so head from remaining string would be a...
来源:https://stackoverflow.com/questions/25618803/functions-as-arguments-to-be-used-in-template-haskell-quote