I have been trying to get some basic dynamic code compilation working using the GHC API by following a tutorial found here.
This code:
import GHC
import GHC.Paths
import DynFlags
import Unsafe.Coerce
main :: IO ()
main =
defaultErrorHandler defaultDynFlags $ do
func <- runGhc (Just libdir) $ do
dflags <- getSessionDynFlags
setSessionDynFlags dflags
target <- guessTarget "Test.hs" Nothing
addTarget target
r <- load LoadAllTargets
case r of
Failed -> error "Compilation failed"
Succeeded -> do
m <- findModule (mkModuleName "Test") Nothing
setContext [] [m]
value <- compileExpr ("Test.print")
do let value' = (unsafeCoerce value) :: String -> IO ()
return value'
func "Hello"
return ()
Should get the print function from another file called Test.hs, load it and run its print function.
I compile the code with ghc version 7.4.1 using the command:
ghc -package ghc --make Api.hs
But receive the following error:
Api.hs:8:25:
Couldn't match expected type `Severity' with actual type `Settings'
Expected type: LogAction
Actual type: Settings -> DynFlags
In the first argument of `defaultErrorHandler', namely
`defaultDynFlags'
In the expression: defaultErrorHandler defaultDynFlags
What am I doing wrong? I have checked the GHC API docs but am not well versed enough in this kind of thing to understand most of it.
The tutorial is out of date. In ghc-7.0.* and previous, the type of defaultErorHandler
was
defaultErrorHandler :: (ExceptionMonad m, MonadIO m) => DynFlags -> m a -> m a
and defaultDynFlags
was just a value.
As of ghc-7.2.*, the type of defaultErrorHandler
is
defaultErrorHandler :: (ExceptionMonad m, MonadIO m) => LogAction -> m a -> m a
defaultDynFlags
is a function
defaultDynFlags :: Settings -> DynFlags
and LogAction
is a synonym
type LogAction = Severity -> SrcSpan -> PprStyle -> Message -> IO ()
In 7.6, it has changed again, we now have
defaultErrorHandler :: (ExceptionMonad m, MonadIO m) => FatalMessager -> FlushOut -> m a -> m a
with
type FatalMessager = String -> IO ()
and FlushOut
being a newtype
wrapper around IO ()
.
I'm not very familiar with the GHC Api (a too fast-moving target for me), so I'm not sure how the working code should look like, but for the 7.2 and 7.4 series, the first argument to defaultErrorHandler
should probably be defaultLogAction
.
Also the type of setContext
has changed, I don't know if what I have does what you want, but it compiles (with 7.4.2; but you also need the ghc-paths
package in addition to ghc
for the GHC.Paths
module) - I haven't tried to run it, though.
import GHC
import GHC.Paths
import DynFlags
import Unsafe.Coerce
main :: IO ()
main =
defaultErrorHandler defaultLogAction $ do
func <- runGhc (Just libdir) $ do
dflags <- getSessionDynFlags
setSessionDynFlags dflags
target <- guessTarget "Test.hs" Nothing
addTarget target
r <- load LoadAllTargets
case r of
Failed -> error "Compilation failed"
Succeeded -> do
m <- findModule (mkModuleName "Test") Nothing
setContext [IIModule m]
value <- compileExpr ("Test.print")
do let value' = (unsafeCoerce value) :: String -> IO ()
return value'
func "Hello"
return ()