Description
As previously recommended by @sellout here, when dhall code is loaded via TemplateHaskell, it is important to call addDependentFile
on the dhall file. Otherwise, changes to that dhall file will appear to be ignored!
The problem
Here is a small example demonstrating the problem.
$ cat ./Main.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Main where
import Dhall.Core
import Dhall.TH
config :: Expr () ()
config = $(staticDhallExpression "./example.dhall")
main :: IO ()
main = do
print config
$ echo 42 > example.dhall
$ ghc ./Main.hs
$ ./Main
NaturalLit 42
So far so good. Let's try changing the configuration:
$ echo 43 > example.dhall
$ ghc ./Main.hs
$ ./Main
NaturalLit 42
Oh no, the configuration change was ignored!
The workaround
One unappealing workaround is to clear away the build artifacts in order to force ghc to rebuild:
$ rm ./Main.o
$ ghc ./Main.hs
$ ./Main
NaturalLit 43
Towards a solution
A better fix is to use addDependentFile
to tell ghc that if example.dhall
changes, it needs to recompile the TemplateHaskell code which reads that file:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Main where
import Dhall.Core
import Dhall.TH
import Language.Haskell.TH.Syntax
config :: Expr () ()
config = $(do
addDependentFile "./example.dhall"
staticDhallExpression "./example.dhall")
main :: IO ()
main = do
print config
ghc now rebuilds when the dhall file changes.
$ echo 42 > example.dhall
$ ghc ./Main.hs
$ ./Main
NaturalLit 42
$ echo 43 > example.dhall
$ ghc ./Main.hs
$ ./Main
NaturalLit 43
Not quite there
Alas, the above does not work in all cases. dhall is a language which supports imports, and thus it is possible for example.dhall
to in turn depend on another dhall file:
$ echo "./other.dhall" > example.dhall
Now our addDependentFile "./example.dhall"
is insufficient; we should also have included addDependentFile "./other.dhall"
!
$ echo 42 > other.dhall
$ ghc ./Main.hs
$ ./Main
42
$ echo 43 > other.dhall
$ ghc ./Main.hs
$ ./Main
42
Proposed solution
Rather than putting the onus on the user of Dhall.TH
to maintain a list of the dependencies of their dhall files, it would make a lot more sense if it was the responsibility of the Dhall.TH
functions to call addDependentFile
on all the imports they encounter in the process of evaluating a dhall expression.