11

I am trying to load and execute module dynamically,

Below is my code

TestModule.hs

module TestModule
        where

evaluate = "Hello !!!"

Invoke.hs

module Invoke
        where

import GHC
import DynFlags
import GHC.Paths (libdir)
import Unsafe.Coerce (unsafeCoerce)
import Data.Dynamic

execFnGhc :: String -> String -> Ghc a
execFnGhc modname fn = do
        mod <- findModule (mkModuleName modname) Nothing
        --setContext [IIModule mod]
        GHC.setContext [ GHC.IIDecl $ (GHC.simpleImportDecl . GHC.mkModuleName $ modname) {GHC.ideclQualified = True} ]
        value <- compileExpr (modname ++ "." ++ fn)
        let value' = (unsafeCoerce value) :: a
        return value'

Main2.hs

import GHC.Paths (libdir)
import GHC
import Invoke
--    import TestModule

main :: IO ()
main = runGhc (Just libdir) $ do
                        str <- execFnGhc "TestModule" "evaluate"
                        return str

When I try to run the program it show me below error

[root@vps mypproj]# ./Main2 
Main2: <command line>: module is not loaded: `TestModule' (./TestModule.hs)

Not sure what I am missing, Can someone please help me resolve this error

Xinus
  • 29,617
  • 32
  • 119
  • 165
  • I don't know a lot about this, but it seems like you can do this much easier with the [plugins](http://hackage.haskell.org/package/plugins) package. – Almanildo Apr 11 '13 at 08:54

2 Answers2

1

My thought would be the problem has something to do with your path,and that the program silently errors when it can't load "TestModule," then complains that the module is not loaded. Have you tried using execFnGhc with a module that is already loaded, and have you tried loading a module that is in GHC naturally, such as Text.Parsec, then executing something in it?

I'd test myself, but I don't see a GHC.Paths library anywhere :/.

Marshall Conover
  • 855
  • 6
  • 24
  • I tried list length, str <- execFnGhc "Data.List" "length [1,2,3]", but it is throwing exception as "Main: Main: panic! (the 'impossible' happened) (GHC version 7.4.2 for x86_64-unknown-linux): no package state yet: call GHC.setSessionDynFlags Please report this as a GHC bug: http://www.haskell.org/ghc/reportabug " – Xinus Apr 21 '13 at 10:00
  • From the error, it looks like before you can use execFnGhc, you need to set "session flags" for GHC. Try looking at the type of GHC.setSessionFlags and maybe seeing if "GHC.getSessionFlags" exists; it looks like GHC needs more information before it can call things. – Marshall Conover Apr 22 '13 at 19:36
0

I was reading the relevant GHC source code recently, and it looks like findModule doesn't work on local modules (TestModule.hs in your case) unless they've already been loaded. (It works on modules in remote packages, however.)

To do GHCi style dynamic loading of compiled modules, your best bet is to use addTarget and load. As was mentioned in the comments, you also need to initialize the session dynamic flags. Here is a working version of your code:

module Invoke
        where

import GHC
import DynFlags
import GHC.Paths (libdir)
import Unsafe.Coerce (unsafeCoerce)
import Data.Dynamic

execFnGhc :: String -> String -> Ghc String
execFnGhc modname fn = do
        dflags <- getDynFlags
        setSessionDynFlags dflags
        let target = Target (TargetModule (mkModuleName modname)) True Nothing
        addTarget target
        load (LoadUpTo (mkModuleName modname))
        mod <- findModule (mkModuleName modname) Nothing
        GHC.setContext [ GHC.IIDecl $ (GHC.simpleImportDecl . GHC.mkModuleName $ modname) {GHC.ideclQualified = True} ]
        value <- compileExpr (modname ++ "." ++ fn)
        let value' = (unsafeCoerce value) :: String
        return value'

What are the parameters of Target? The first is the module name; the second is whether or not we should be allowed to load object code, or always interpret the module; the last is an optional string buffer which you could use to override the source code in the actual file (it's Nothing because we don't need this.)

How did I figure this out? I looked at the code that GHCi uses to implement this in the GHC source code, as well as the compiler/main/GHC.hs. I've found this is the most reliable way to figure out how to get the GHC API to do what you want.

Confusing? The GHC API was not so much designed as accreted...

Edward Z. Yang
  • 26,325
  • 16
  • 80
  • 110