You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在LTS Haskell 20.20(GHC 9.2.7)中动态编译导入模块?

动态编译并调用Haskell模块的问题

需求

实现一个Haskell程序,能够动态生成Haskell模块(.hs文件),编译后将其导入到当前应用中并调用其中的函数。

用户现有代码

Main.hs

import qualified GhcApiWrap as Ghw

main :: IO ()
main = Ghw.msLoadModuleAndExecute "../dyn/" "DynExample.hs" "nFromChar" 'A'

GhcApiWrap.hs

module GhcApiWrap
    (
        msLoadModuleAndExecute
    ) where

import GHC
--import GHC.Paths (libdir)  

msLoadModuleAndExecute :: String -> String -> String -> Char -> IO ()
msLoadModuleAndExecute _ _ _ _ = do
    value' <- runGhc (Just "./src/") $ do
            dflags <- getSessionDynFlags
            setSessionDynFlags $ dflags { 
                ghcLink   = LinkInMemory, 
                ghcMode = CompManager,
                objectDir   = Just "../dyn/",
                hiDir   = Just "../dyn/"
                }
            target <- guessTarget ("DynExample.hs") Nothing
            setTargets [target]
            ret <- load LoadAllTargets
            case ret of
                Succeeded -> do
                    importDecl_RdrName <- parseImportDecl $ "import DynExample"
                    setContext [IIDecl importDecl_RdrName]

                    value <- dynCompileExpr ("DynExample.nFromChar")

                    return value
                _       -> 
                    return undefined
    print $ value'

DynExample.hs

module DynExample
    (
        nFromChar
    ) where

nFromChar :: Char -> Int
nFromChar _ = 33

运行错误

代码可编译但无法正常运行,输出错误信息:

ExprmntGhcApi-exe: Missing file: src/settings

用户注释掉import GHC.Paths (libdir)是因为该模块在当前环境中不存在,尝试用(Just "./src/")替代(Just libdir),但多个网上流传的示例代码在GHC 9.2.7中均无法正常工作。

核心问题

在Stack环境(解析器对应LTS 20.20,即GHC 9.2.7)中,能否通过上述方式动态调用DynExample.nFromChar?如果可行,如何实现类型为msLoadModuleAndExecute :: String -> String -> String -> Char -> IO Int的函数?

环境配置

当前使用Stack,resolver配置如下:

resolver:
  url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/20.yaml

对应GHC版本为9.2.7。


解决方法

1. 修复libdir获取问题

GHC 9.x版本中GHC.Paths模块不再默认提供,需要手动添加ghc-paths包到项目依赖。在项目的package.yaml或.cabal文件中添加:

dependencies:
  - base >= 4.16 && < 5
  - ghc
  - ghc-paths

执行stack build安装依赖后,恢复import GHC.Paths (libdir),并将runGhc的参数改为(Just libdir)——这里的libdir是GHC的系统库目录,而非项目的src目录,GHC需要它来加载自身的配置文件。

2. 调整动态编译与类型转换逻辑

修改GhcApiWrap.hs,实现类型安全的动态调用:

module GhcApiWrap
    (
        msLoadModuleAndExecute
    ) where

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

msLoadModuleAndExecute :: String -> String -> String -> Char -> IO (Maybe Int)
msLoadModuleAndExecute objDir modulePath funcName arg = runGhc (Just libdir) $ do
    dflags <- getSessionDynFlags
    -- 调整动态编译的参数,添加模块搜索路径
    let newDflags = dflags {
            ghcLink = LinkInMemory,
            ghcMode = CompManager,
            objectDir = Just objDir,
            hiDir = Just objDir,
            importPaths = [objDir]
        }
    -- 应用新的动态标志
    (_updatedDflags, _) <- setSessionDynFlags newDflags
    -- 设置编译目标
    target <- guessTarget modulePath Nothing
    setTargets [target]
    -- 加载并编译目标模块
    ret <- load LoadAllTargets
    case ret of
        Succeeded -> do
            -- 解析导入声明并设置上下文
            let moduleName = takeWhile (/= '.') modulePath
            importDecl <- parseImportDecl $ "import " ++ moduleName
            setContext [IIDecl importDecl]
            -- 编译带参数的函数调用表达式
            dynVal <- dynCompileExpr $ funcName ++ " " ++ show arg
            -- 安全转换为目标类型
            return $ fromDynamic dynVal
        _ -> return Nothing

3. 调整Main.hs的调用逻辑

import qualified GhcApiWrap as Ghw

main :: IO ()
main = do
    result <- Ghw.msLoadModuleAndExecute "../dyn/" "DynExample.hs" "nFromChar" 'A'
    print result

关键说明

  • libdir是GHC的系统库目录,由ghc-paths提供,必须正确传入runGhc,否则GHC无法找到自身的配置文件(如settings)。
  • importPaths需要包含目标模块所在的目录,确保GHC能找到要编译的.hs文件。
  • 使用fromDynamic进行安全类型转换,若类型不匹配会返回Nothing;如果确认类型完全匹配,也可以用unsafeCoerce直接转换函数类型:
    -- 直接获取函数并调用的方式
    dynFunc <- dynCompileExpr funcName
    let func = unsafeCoerce dynFunc :: Char -> Int
    return $ Just (func arg)
    
    注意:unsafeCoerce存在运行时风险,仅在确保类型绝对匹配时使用。

内容的提问来源于stack exchange,提问作者Jörg Brüggmann

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 03:21:03