Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly communicate compile-time information to Template Haskell functions?

I need to communicate some information from compile scripts into Template Haskell. Currently the compile scripts keep the information in the system environment, so I just read it using System.Environment.getEnvironment wrapped in runIO. Is there a better way, such as passing some arguments to ghc (similar to -D... for the C pre-processor), or perhaps something specifically designed for this purpose in TH?

like image 901
Petr Avatar asked Oct 30 '13 09:10

Petr


1 Answers

Since so many people are interested in the question, I'll add my current approach, perhaps somebody will find it useful. Probably the best way would be if TH allowed to read -D parameters on GHC's command line, but it seems nothing like this is currently implemented.

A simple module allows TH to read compile-time environment. A helper function also allows to read files; for example read the path of a configuration file from the environment and then read the file.

{-# LANGUAGE TemplateHaskell #-} module THEnv     (     -- * Compile-time configuration       lookupCompileEnv     , lookupCompileEnvExp     , getCompileEnv     , getCompileEnvExp     , fileAsString     ) where  import Control.Monad import qualified Data.Text as T import qualified Data.Text.IO as T import Language.Haskell.TH import Language.Haskell.TH.Syntax (Lift(..)) import System.Environment (getEnvironment)  -- Functions that work with compile-time configuration  -- | Looks up a compile-time environment variable. lookupCompileEnv :: String -> Q (Maybe String) lookupCompileEnv key = lookup key `liftM` runIO getEnvironment  -- | Looks up a compile-time environment variable. The result is a TH -- expression of type @Maybe String@. lookupCompileEnvExp :: String -> Q Exp lookupCompileEnvExp = (`sigE` [t| Maybe String |]) . lift <=< lookupCompileEnv     -- We need to explicly type the result so that things like `print Nothing`     -- work.  -- | Looks up an compile-time environment variable and fail, if it's not -- present. getCompileEnv :: String -> Q String getCompileEnv key =   lookupCompileEnv key >>=   maybe (fail $ "Environment variable " ++ key ++ " not defined") return  -- | Looks up an compile-time environment variable and fail, if it's not -- present. The result is a TH expression of type @String@. getCompileEnvExp :: String -> Q Exp getCompileEnvExp = lift <=< getCompileEnv  -- | Loads the content of a file as a string constant expression. -- The given path is relative to the source directory. fileAsString :: FilePath -> Q Exp fileAsString = do   -- addDependentFile path -- works only with template-haskell >= 2.7   stringE . T.unpack . T.strip <=< runIO . T.readFile 

It can be used like this:

{-# LANGUAGE TemplateHaskell #-} import THEnv main = print $( lookupCompileEnvExp "DEBUG" ) 

Then:

  • runhaskell Main.hs prints Nothing;
  • DEBUG="yes" runhaskell Main.hs prints Just "yes".
like image 65
Petr Avatar answered Oct 09 '22 17:10

Petr