Wie können Informationen zur Kompilierungszeit ordnungsgemäß an Vorlagen-Haskell-Funktionen übertragen werden?

70

Ich muss einige Informationen aus Kompilierungsskripten in Template Haskell übermitteln. Derzeit behalten die Kompilierungsskripte die Informationen in der Systemumgebung bei, daher lese ich sie nur mit System.Environment.getEnvironmentWraps in runIO. Gibt es einen besseren Weg, wie das Übergeben einiger Argumente ghc(ähnlich wie -D...beim C-Vorprozessor) oder vielleicht etwas, das speziell für diesen Zweck in TH entwickelt wurde?

Petr
quelle
9
Das Lesen dieser Informationen aus einer externen Datei und die Verwendung addDependentFile, um diese Datei bekannt zu machen, ghc --makeist eine offensichtliche Alternative. Was sind die Probleme, die Sie mit dem aktuellen Schema haben?
Mikhail Glushenkov
2
@MikhailGlushenkov Tatsächlich passiert die Umgebung nur das Stammverzeichnis des Projektverzeichnisses und dann werden weitere Informationen aus einer Datei gelesen. Also addDependentFilewird für meinen Fall hilfreich sein. Das aktuelle Schema funktioniert, ich wollte nur wissen, ob es eine andere kanonische Methode gibt.
Petr
5
Sie können die locationFunktion auch verwenden , um das Stammverzeichnis des Projektverzeichnisses abzurufen (vorausgesetzt, Sie kennen den relativen Pfad vom aktuellen Modul zum Stammverzeichnis). Hier ist ein Beispiel .
Mikhail Glushenkov
Sie könnten -XCPP mit Template-Haskell verwenden, aber es scheint, als würde Ihr Weg besser funktionieren.
Aavogt
1
Möchten Sie, dass jemand einem Benutzer seine eigene Konfigurationsdatei auswählt, indem er beispielsweise einen Dateipfad über die Befehlszeile übergibt?
user3125280

Antworten:

14

Da sich so viele Menschen für die Frage interessieren, werde ich meinen aktuellen Ansatz hinzufügen, vielleicht findet ihn jemand nützlich. Der wahrscheinlich beste Weg wäre, wenn TH -DParameter in der Befehlszeile von GHC lesen darf , aber es scheint, dass derzeit nichts dergleichen implementiert ist.

Ein einfaches Modul ermöglicht es TH, die Umgebung zur Kompilierungszeit zu lesen. Eine Hilfsfunktion ermöglicht auch das Lesen von Dateien. Lesen Sie beispielsweise den Pfad einer Konfigurationsdatei aus der Umgebung und dann die Datei.

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

Es kann folgendermaßen verwendet werden:

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

Dann:

  • runhaskell Main.hsDrucke Nothing;
  • DEBUG="yes" runhaskell Main.hsdruckt Just "yes".
Petr
quelle
3

Es sieht so aus, als ob Sie versuchen, dies hier zu tun . Die Option -D in ghc scheint eine Variable für die Kompilierungszeit zu definieren.

Hier ist zum gleichen Thema eine Frage , die auch den anderen Teil Ihrer Frage zu beantworten scheint. Nach allem, was ich sagen kann, machen Sie für die bedingte Kompilierung so etwas wie:

    #ifdef MACRO_NAME
    //Do stuff here
    #endif
violet_white
quelle
1
Wie ich in den Kommentaren sagte, möchte ich CPP und bedingte Kompilierung nicht verwenden. Ich kann es nicht gebrauchen. Ich möchte nur Informationen an Template Haskell weitergeben. Die -DOption wäre schön, wenn es eine Möglichkeit gäbe, sie in TH ohne CPP zu lesen.
Petr
1
Dies dient wiederum zur bedingten Kompilierung im Hashkell- Code. Ich kenne nichts anderes als das Definieren von Makros mit -D (möglicherweise mit einem festgelegten Wert), und dann könnten Sie den Wert in Ihrem Hashkell ​​überprüfen und es könnte funktionieren. Ich weiß nicht genug über Haskell, um sicher zu sein.
violet_white