Hoe kan ik compile-time informatie correct doorgeven aan Template Haskell-functies?

Ik moet wat informatie van compileerscripts doorgeven aan Template Haskell. Momenteel houden de compileerscripts de informatie in de systeemomgeving, dus ik lees het gewoon met behulp van System.Environment.getEnvironmentverpakt in runIO. Is er een betere manier, zoals het doorgeven van enkele argumenten aan ghc(vergelijkbaar met -D...voor de C pre-processor), of misschien iets dat hier speciaal voor is ontworpen doel in TH?


Antwoord 1, autoriteit 100%

Omdat zoveel mensen geïnteresseerd zijn in de vraag, zal ik mijn huidige aanpak toevoegen, misschien vindt iemand het nuttig. Waarschijnlijk zou de beste manier zijn als TH de -D-parameters op de opdrachtregel van GHC zou mogen lezen, maar het lijkt erop dat zoiets momenteel niet is geïmplementeerd.

Een eenvoudige module stelt TH in staat om de compile-time-omgeving te lezen. Een helperfunctie maakt het ook mogelijk om bestanden te lezen; lees bijvoorbeeld het pad van een configuratiebestand uit de omgeving en lees vervolgens het bestand.

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

Het kan als volgt worden gebruikt:

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

Dan:

  • runhaskell Main.hsdrukt Nothingaf;
  • DEBUG="yes" runhaskell Main.hsdrukt Just "yes"af.

Antwoord 2, autoriteit 21%

Het lijkt erop wat u probeert te doen hierlijkt de -D optie in ghc een compileertijdvariabele te definiëren.

Hier, over hetzelfde onderwerp is een vraagdie ook lijkt beantwoord het andere deel van je vraag.
Voor zover ik kan zien, doe je om voorwaardelijke compilatie te doen zoiets als:

   #ifdef MACRO_NAME
    //Do stuff here
    #endif

Other episodes