Search code examples
haskellcompilationevaluationthunk

Have Haskell expand certain thunks at compile time?


Is there a way to have Haskell expand certain thunks at run time. For example, say I have

--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

How could I have it evaluate goldRatio at compile time. Like for example, with

{-# EVALUATE goldRatio #-}

It would only need to be to weak head form, since Control.Deepseq.force could handle the rest. I hear template haskell can do this, but I do not know it well.

Note: I am using GHC at the moment.


Solution

  • This is pretty simple with template haskell. Firstly, define the code in one module:

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

    Then create the value using that code with template haskell in another module. You have to do it in another module as template haskell definitions cannot call functions defined in the same module.

    {-# LANGUAGE TemplateHaskell #-}
    import Test
    import Language.Haskell.TH
    import Data.Ratio
    
    goldRatio :: Double
    goldRatio = $(litE (rationalL (toRational $ fib 21 / fib 20)))
    

    Now compiling will take longer, however goldRatio will now be a fixed value and compute instantly at runtime. It runs as if you typed goldRatio = 1.6180339985218033 in the source code. Example use:

    > goldRatio
    1.6180339985218033