Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GHC API - How to dynamically load Haskell code from a compiled module using GHC 7.2?

I have an existing Haskell function that uses the GHC API to dynamically load compiled code from a module. It is based on the code from the blog post Dynamic Compilation and Loading of Modules in Haskell.

The code works fine in GHC 7.0, but had to be slightly modified to compile in GHC 7.2, because the GHC API changed.

The code now throws a runtime error in GHC 7.2:

mkTopLevEnv: not a home module (module name):(function name)

The code is

evalfuncLoadFFI String moduleName, 
                String externalFuncName, 
                String internalFuncName = do

  result <- liftIO $ defaultRunGhc $ do
    dynflags <- GHC.getSessionDynFlags
    _ <- GHC.setSessionDynFlags dynflags
    m <- GHC.findModule (GHC.mkModuleName moduleName) Nothing

--------------------------------------------------------    
-- The following code works fine in GHC 7.0.4:
--
--  GHC.setContext [] [(m, Nothing)]
--
-- This new code attempts to set context to the module, 
-- but throws an error in GHC 7.2:
--
    (_,oi) <- GHC.getContext
    GHC.setContext [m] oi
--------------------------------------------------------

    fetched <- GHC.compileExpr (moduleName ++ "." ++ externalFuncName)
    return (Unsafe.Coerce.unsafeCoerce fetched :: [LispVal] -> IOThrowsError LispVal)
  defineVar env internalFuncName (IOFunc result)

For reference, the full code is available online in FFI.hs (github.com).

Does anyone have any idea how to fix or work around this problem?

Also, could this be caused by the new Safe Haskell changes in GHC 7.2, or is it just due to modifications to the GHC API?

like image 989
Justin Ethier Avatar asked Feb 08 '12 17:02

Justin Ethier


1 Answers

The current module context is reserved for modules that are currently being compiled, i.e. when you specify modules in the context, they must explicitly not be external.

Instead, you should specify the wanted module as an import, in the second argument of setContext. This can be done like so:

GHC.setContext []
  -- import qualified Module
  [ (GHC.simpleImportDecl . GHC.mkModuleName $ moduleName)
    { GHC.ideclQualified = True
    }
  -- -- import qualified Data.Dynamic
  -- , (GHC.simpleImportDecl . GHC.mkModuleName $ "Data.Dynamic")
  --   { GHC.ideclQualified = True
  --   }
  ]
fetched <- GHC.compileExpr $ moduleName ++ "." ++ externalFuncName
return . unsafeCoerce $ fetched
-- or:
-- fetched <- GHC.dynCompileExpr $ moduleName ++ "." ++ externalFuncName
-- return . fromDynamic (error "Illegal type cast") $ fetched

PS: it might be a good idea to use GHC.dynCompileExpr instead, so that you can avoid the unsafeCoerce. You must add a qualified import for Data.Dynamic in the context for it to work, but a Data.Dynamic.Dynamic value is generally nicer to work with, since you can handle type errors more gracefully. I've added the code for that as comments in the above code.


Update

And here is the syntax for GHC 7.4.1:

GHC.setContext  
  -- import qualified Module
  [ GHC.IIDecl $ 
    (GHC.simpleImportDecl . GHC.mkModuleName $ moduleName)
    {GHC.ideclQualified = True}
  ]
like image 66
dflemstr Avatar answered Nov 20 '22 20:11

dflemstr