Ghc 7.4.2, Динамически вызывающие модули

Я пытаюсь загрузить и выполнить модуль динамически,

Ниже мой код

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

Когда я пытаюсь запустить программу, я показываю ниже ошибку

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

Не уверен, что мне не хватает, может кто-то помочь мне решить эту ошибку.

Ответ 1

Моя мысль была бы связана с тем, что проблема имеет какое-то отношение к вашему пути, и что программа без ошибок совершает ошибки, когда она не может загрузить "TestModule", а затем жалуется, что модуль не загружен. Пробовали ли вы использовать execFnGhc с уже загруженным модулем и попробовали ли вы загружать модуль, который находится в GHC, например, Text.Parsec, а затем что-то что-то в нем делать?

Я бы проверил себя, но я не вижу библиотеки GHC.Paths где угодно:/.

Ответ 2

Недавно я читал соответствующий исходный код GHC, и похоже, что findModule не работает с локальными модулями (TestModule.hs в вашем случае), если они уже не были загружены. (Однако он работает с модулями в удаленных пакетах.)

Чтобы выполнить динамическую загрузку скомпилированных модулей в стиле GHCi, лучше всего использовать addTarget и load. Как уже упоминалось в комментариях, вам также необходимо инициализировать динамические флаги сессии. Вот рабочая версия вашего кода:

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'

Каковы параметры Target? Первое - это имя модуля; второй - это то, нужно ли нам разрешать загружать объектный код или всегда интерпретировать модуль; последний - необязательный строковый буфер, который вы можете использовать для переопределения исходного кода в фактическом файле (это Nothing, потому что нам это не нужно).

Как я понял это? Я просмотрел код, который GHCi использует для реализации этого в исходном коде GHC, а также compiler/main/GHC.hs. Я нашел, что это самый надежный способ выяснить, как заставить API GHC делать то, что вы хотите.

Непонятный? API GHC был не столько сконструирован, как расшифрован...