Ответ на этот вопрос был дан мне в другом месте.GHC API способен сделать это.Вот две функции, одна из которых компилирует Target.hs
, а другая обращается к Target.accessMe
(и больше не требует наличия там исходного кода модуля Target
).
import GHC
import DynFlags
compile :: String -> IO SuccessFlag
compile name = defaultRunGhc $ do
dynflags <- getSessionDynFlags
let dynflags' = dynflags -- You can change various options here.
setSessionDynFlags dynflags'
-- (name) can be "Target.hs", "Target", etc.
target <- guessTarget name Nothing
addTarget target
load LoadAllTargets -- Runs something like "ghc --make".
Это функция, которая компилирует данный модуль и возвращает, была ли успешной компиляция или нет.Он использует вспомогательную функцию defaultRunGhc
, которая определена как:
import GHC.Paths (libdir)
defaultRunGhc :: Ghc a -> IO a
defaultRunGhc = defaultErrorHandler defaultDynFlags . runGhc (Just libdir)
А теперь функция для извлечения значения из скомпилированного модуля.Исходный код модуля не обязательно должен присутствовать на этом этапе.
import Unsafe.Coerce (unsafeCoerce)
fetch :: String -> String -> IO Int -- Assumes we are fetching an Int value.
fetch name value = defaultRunGhc $ do
-- Again, you can change various options in dynflags here, as above.
dynflags <- getSessionDynFlags
let m = mkModule (thisPackage dynflags) (mkModuleName name)
setContext [] [(m, Nothing)] -- Use setContext [] [m] for GHC<7.
fetched <- compileExpr (name ++ "." ++ value) -- Fetching "Target.accessMe".
return (unsafeCoerce fetched :: Int)
И все!