Расширить ли Haskell определенные преобразователи во время компиляции?

Есть ли способ заставить Haskell расширять определенные преобразователи во время выполнения. Например, скажем, у меня есть

--Purposely inefficient code for demonstration
fib 0=0
fib 1=1
fib n=fib n=fib (n-1) + fib (n-2)
goldRatio=fib 100 / fib 101

Как я мог оценить goldRatio во время компиляции. Как, например, с

{-# EVALUATE goldRatio #-}

Это должно было быть только в слабой форме головы, так как Control.Deepseq.force мог справиться с остальным. Я слышал, что шаблон Haskell может это сделать, но я не очень хорошо это знаю.

Примечание. В настоящее время я использую GHC.


person PyRulez    schedule 30.03.2014    source источник


Ответы (1)


Это довольно просто с шаблоном haskell. Во-первых, определите код в одном модуле:

module Test where
--Purposely inefficient code for demonstration
fib 0=0
fib 1=1
fib n=fib (n-1) + fib (n-2)

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

{-# LANGUAGE TemplateHaskell #-}
import Test
import Language.Haskell.TH
import Data.Ratio

goldRatio :: Double
goldRatio = $(litE (rationalL (toRational $ fib 21 / fib 20)))

Теперь компиляция займет больше времени, однако goldRatio теперь будет фиксированным значением и будет вычисляться мгновенно во время выполнения. Он работает так, как если бы вы набрали goldRatio = 1.6180339985218033 в исходном коде. Пример использования:

> goldRatio
1.6180339985218033
person David Miani    schedule 31.03.2014
comment
goldRatio = $(let x = fib 20 / fib 21 in [| x |]) тоже работает. Это немного бестолково в том смысле, что привязка let необходима, но результат не требует дополнительного импорта. Пока вам не нужно написать экземпляр Lift, чтобы он работал. - person Carl; 31.03.2014