Haskell中的动态编译 GHC API错误

7

我一直在尝试使用GHC API实现基本的动态代码编译,并按照这里的教程进行操作。

以下是代码:

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

从名为Test.hs的另一个文件获取print函数,加载并运行它的print函数。
我使用以下命令使用ghc版本7.4.1编译代码:
ghc -package ghc --make Api.hs

但是收到了以下错误:
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

我做错了什么?我已经查看了GHC API文档,但是对这种东西不太熟悉,大部分内容都不理解。

2个回答

6

这篇教程已经过时。在GHC-7.0.*及之前的版本中,defaultErrorHandler的类型为

defaultErrorHandler :: (ExceptionMonad m, MonadIO m) => DynFlags -> m a -> m a

而且defaultDynFlags只是一个值。

从ghc-7.2.*开始,defaultErrorHandler的类型为

defaultErrorHandler :: (ExceptionMonad m, MonadIO m) => LogAction -> m a -> m a

defaultDynFlags是一个函数

defaultDynFlags :: Settings -> DynFlags

“LogAction”是一个同义词

type LogAction = Severity -> SrcSpan -> PprStyle -> Message -> IO ()

在7.6版本中,它再次发生了变化,现在我们拥有:
defaultErrorHandler :: (ExceptionMonad m, MonadIO m) => FatalMessager -> FlushOut -> m a -> m a

使用

type FatalMessager = String -> IO ()

FlushOut是一个围绕IO()newtype包装器。

我对GHC Api不是很熟悉(对我来说太快了),所以我不确定工作代码应该是什么样子的,但对于7.2和7.4系列,defaultErrorHandler的第一个参数可能应该是defaultLogAction

setContext的类型也已更改,我不知道我拥有的是否符合您的要求,但它可以编译(使用7.4.2;但除了ghc之外,您还需要ghc-paths软件包用于GHC.Paths模块)-虽然我没有尝试运行它。

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

1
谢谢,这个似乎编译得很好。然而,当我运行它时,第一次它能正常工作——它编译了Test.hs文件并运行打印函数以打印出单词“Hello”。但是,如果我尝试第二次运行它,我会收到错误消息“mkTopLevEnv:not interpreted main:Test”。 - Craig Innes
1
看起来它不能与编译的模块一起使用。如果你在运行之间删除.hi.o文件,它可能会多次工作。但这有点不方便,不是吗?所以你需要一种可以处理编译模块的方法。不幸的是,我不太了解API,无法告诉你如何做到这一点。也许这可以成为你下一个问题。 - Daniel Fischer
1
我经过多次尝试终于解决了这个问题:有两件事对我有帮助:(1)使用´do {dflags <- getSessionDynFlags;setSessionDynFlags $ dflags {hscTarget = HscInterpreted,ghcLink = LinkInMemory}´以便每次运行重新加载上下文,(2)使用IIDecl而不是IIModule使其更少出错且更安全。此外,parseImportDecl很好用,可以获取IIDecl。如果需要更多信息,请问我,现在我需要睡觉了。 - worldsayshi

2

这里是一个动态加载的完整示例,也可以在这里找到:

DynLoad.hs

-----------------------------------------------------------------------------
-- | Example for loading Haskell source code dynamically using the GHC api
-- Useful links:
-- GHC api:
-- http://www.haskell.org/ghc/docs/latest/html/libraries/ghc/GHC.html
-- Wiki:
-- http://www.haskell.org/haskellwiki/GHC/As_a_library
-----------------------------------------------------------------------------
module DynLoad where

import GHC
import GhcMonad (liftIO)
import GHC.Paths (libdir)
import Name (getOccString)
import Data.Dynamic (fromDyn)

-- |  List all exports of this module
--    and evaluate a symbol from a module DynTest 
main = 
  runGhc (Just libdir) $ do
    putString ":::Display exports of modules:::"
    modSums <- initSession ["DynLoad","DynTest"]
    let thisModSum = head modSums
    exports <- listExports thisModSum
    mapM_ putString exports

    putString ":::Evaluate a name from module DynTest:::"
    importDecl_RdrName <- parseImportDecl "import DynTest as D"
    setContext [IIDecl importDecl_RdrName]
    dynVal <- dynCompileExpr "D.aString"
    liftIO $ print $ (fromDyn dynVal "nope-nothing")

-- | Init interactive session and load modules
initSession modStrNames = do
  dflags <- getSessionDynFlags
  setSessionDynFlags $ dflags {
    hscTarget = HscInterpreted
    , ghcLink   = LinkInMemory
    }
  targets <- mapM
              (\modStrName -> do
                  putString modStrName
                  target <- guessTarget ("*"++modStrName++".hs") Nothing
                  return target
              ) modStrNames
  setTargets targets
  load LoadAllTargets
  modSums <- mapM
              (\modStrName -> do
                  putString modStrName
                  modSum <- getModSummary $ mkModuleName modStrName
                  return $ ms_mod modSum
              ) modStrNames
  return modSums

-- | List exported names of this or a sibling module
listExports mod = do
  maybeModInfo <- getModuleInfo mod
  case maybeModInfo of
    (Just modInfo) -> do
      let expNames = modInfoExports modInfo
          expStrNames = map getOccString expNames
      return expStrNames
    _ -> return []

-- | Util for printing
putString = liftIO . putStrLn

以下是示例文件内容:

DynTest.hs

这是一个加载示例文件的例子:

module DynTest where


aString = "Hello"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接