Search code examples
haskellghcdynamic-compilation

Dynamic Compilation in Haskell GHC API Error


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.


Solution

  • 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 ()