Можно ли преобразовать рекурсивную функцию TH в эквивалентную форму, которая будет компилироваться? Следующее определение не работает, потому что для компиляции fact
вы должны сначала скомпилировать fact
.Написание рекурсивных шаблонов haskell-функций
fact :: ExpQ -> ExpQ
fact n = [|
case $n of
1 -> 1
_ -> $n * $(fact [| $n - 1 |]) |]
Этот простой пример легко решается (fact n = [| product [ 1.. $n] |]
), но в общем случае, если это не представляется возможным переписать данную функцию в виде петли, может рекурсивная функция TH быть определена? Есть ли даже один пример, для которого это выполнимо?
Чтобы уточнить для будущих читателей: этот вопрос конкретно о написании рекурсивный TH функции - не о 'как сплайсировать факторную функцию'. не
Ответ на мой вопрос оказался удивительно простым:
{-# LANGUAGE TemplateHaskell #-}
import Control.Monad.Fix (fix)
import Language.Haskell.TH
fact = [| \f x -> $([|
case x of
1 -> 1
_ -> f $([| x - 1 |]) * x |]) |]
factorial = [| fix $fact |]
fact
может быть скомпилирован, так как он больше не рекурсивный, и [| fix $fact |]
составляется на позже время, так что не более бесконечные рекурсивные определения.
Эта версия fact
выглядит несколько иначе, чем оригинал, но вы можете написать новый fact
точно так, как старый и превратить его позже:
fact' recurse n = [|
case $n of
1 -> 1
_ -> $n * $(recurse [| $n - 1 |]) |]
fact = [| \x -> $((\f -> [| \x -> $(fact (\x -> [| $f $x |]) [| x |]) |]) [| x |]) |]
Вы просто сплайсируете всю функцию 'fact'. Если бы это была цель, я мог бы написать свой код без использования TH вообще. При таком подходе я не могу выполнять никаких манипуляций с использованием функции TH внутри этой функции, потому что внутреннее 'n' не может выйти из ее области. – user2407038
В общем, дело в том, что вам нужно преобразовать свой код, чтобы рекурсия была во время выполнения, используя трюки, подобные этому. Если ваша рекурсия не будет снижаться во время компиляции, ваш код не сможет работать. –
Я добавил пример выполнения рекурсии, которая заканчивается в выражении TH, чтобы проиллюстрировать, что ваша проблема не прерывается, а не сама рекурсия. –