Функции в качестве аргументов для использования в цитате шаблона haskell

Это частично продолжение Поднять экземпляр для функции?. Однако ответ заключается в том, чтобы либо глобально определить функцию, либо переписать ее внутри цитаты. Однако мы будем часто использовать foo с различными функциями для f из области действия let. Это делает практически невозможным определение нескольких глобальных версий f. Последнее решение, заключающееся в написании нашей функции в кавычках, кажется эквивалентным написанию подъема функций.

Итак, есть ли способ поднять функции, взятые в качестве аргументов, для использования в шаблонной цитате Haskell?


очень надуманный пример:

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

Не получится с:

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]

person jek    schedule 02.09.2014    source источник


Ответы (2)


Подъемные функции невозможны. Однако есть две возможные альтернативы, которые могут подойти для вас:

Поднимите только результат функции, примените функцию во время компиляции

В вашем особом случае, поскольку вы знаете во время компиляции как x, так и f, вы можете просто вычислить f x во время компиляции и только склеить результат:

foo :: (Int->Int) -> Int -> ExpQ
foo f x  = [| $(lift $ f x) |]
--       = lift $ f x
-- foo f = lift . f

Это не изменяет сигнатуру типа f, но требует, чтобы вы знали все аргументы, которые хотите передать f. Вам нужно будет импортировать Language.Haskell.TH.Syntax для функции lift.

Передать выражение для функции в качестве аргумента

Если вы не можете использовать первое решение, есть другая альтернатива. Вместо того, чтобы передавать функцию, вы теперь передаете соединение для функции в качестве аргумента:

foo :: ExpQ -> Int -> ExpQ
foo f x = [| $f x |]

Есть два недостатка: во-первых, вы теряете безопасность типов, потому что не проверяется, действительно ли соединение расширяется до чего-то, что можно применить к Int. И вам нужно изменить свой код вызова, например:

g :: ExpQ
g = 
    let 
        f =  [| \x -> x+1 |]
        f' = [| \x' -> $f x' + 1 |]
    in foo f' 0
person bennofs    schedule 02.09.2014

Лучшим ответом на это является передача выражения в функцию, которая сначала оценивает эти выражения, а затем решает внешнюю функцию, например: head ( Drop 3 "Sikandar") Теперь здесь head - это функция, и отбросьте также функцию, но с выражением, поэтому первая оценка будет выполняться во внутренней функции, поэтому результатом для этого будет drop, который отбросит оттуда 3 символа "andar", и он передаст этот результат в голову, поэтому голова из оставшейся строки будет...

person Mr. Sikander Ali Qamaruddin    schedule 18.10.2018