Functions as arguments to be used in template haskell quote

十年热恋 提交于 2020-01-03 16:46:14

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!