Dynamically loading compiled Haskell module - GHC

2019-03-25 02:28发布

问题:

I'm trying to dynamically compile and load Haskell modules using GHC API. I understand the API fluctuates quite a bit from on one version to another so I'm specifically talking about GHC 7.6.*.

I have tried running the same code on MacOS and Linux. In both cases the Plugin module compiles fine but gives the following error on load: Cannot add module Plugin to context: not interpreted

The problem is similar to the one in this where the module would only load if it was compiled in the same run of the host program.

-- Host.hs: compile with ghc-7.6.*
-- $ ghc -package ghc -package ghc-paths Host.hs
-- Needs Plugin.hs in the same directory.
module Main where

import GHC
import GHC.Paths ( libdir )
import DynFlags
import Unsafe.Coerce

main :: IO ()
main =
    defaultErrorHandler defaultFatalMessager defaultFlushOut $ do
      result <- runGhc (Just libdir) $ do
        dflags <- getSessionDynFlags
        setSessionDynFlags dflags
        target <- guessTarget "Plugin.hs" Nothing
        setTargets [target]
        r <- load LoadAllTargets
        case r of
          Failed    -> error "Compilation failed"
          Succeeded -> do
            setContext [IIModule (mkModuleName "Plugin")]
            result <- compileExpr ("Plugin.getInt")
            let result' = unsafeCoerce result :: Int
            return result'
      print result

And the plugin:

-- Plugin.hs
module Plugin where

getInt :: Int
getInt = 33

回答1:

The problem is that you're using IIModule. This indicates that you want to bring the module and everything in it, including non-exported stuff into the context. It's essentially the same as :load with an asterisk in GHCi. And as you've noticed, this only works with interpreted code since it let's you "look inside" the module.

But that's not what you need here. What you want is to load it as if you used :module or an import declaration, which works with compiled modules. For that, you use IIDecl which takes an import declaration which you can make with simpleImportDecl:

setContext [IIDecl $ simpleImportDecl (mkModuleName "Plugin")]