大多数情况直接来自提示示例。我想做的是使用模块和导入等初始化解释器,并以某种方式保留它。稍后(用户事件或其他事件),我希望能够以该初始化状态调用一个函数并多次解释一个表达式。因此,在代码的--split here位置,我希望将init上面的代码和下面的代码包含在一个接受表达式并对其进行解释的新函数中。

module Main where
import Language.Haskell.Interpreter
import Test.SomeModule

main :: IO ()
main = do r <- runInterpreter testHint
          case r of
            Left err -> printInterpreterError err
            Right () -> putStrLn "Done."
          -- Right here I want to do something like the following
          -- but how do I do testInterpret thing so it uses the
          -- pre-initialized interpreter?
          case (testInterpret "expression one")
            Left err -> printInterpreterError err
            Right () -> putStrLn "Done."
          case (testInterpret "expression two")
            Left err -> printInterpreterError err
            Right () -> putStrLn "Done."

testHint :: Interpreter ()
testHint =
    do
      loadModules ["src/Test/SomeModule.hs"]
      setImportsQ [("Prelude", Nothing), ("Test.SomeModule", Just "SM")]
      say "loaded"
      -- Split here, so what I want is something like this though I know
      -- this doesn't make sense as is:
      -- testExpr = Interpreter () -> String -> Interpreter ()
      -- testExpr hintmonad expr = interpret expr
      let expr1 = "let p1o1 = SM.exported undefined; p1o2 = SM.exported undefined; in p1o1"
      say $ "e.g. typeOf " ++ expr1
      say =<< typeOf expr1


say :: String -> Interpreter ()
say = liftIO . putStrLn

printInterpreterError :: InterpreterError -> IO ()
printInterpreterError e = putStrLn $ "Ups... " ++ (show e)

最佳答案

我无法理解您的问题。我对提示也不太熟悉。但我会努力的。

据我所知,Interpreter monad只是IO周围的一个简单的状态包装器-它仅存在,以便您可以说例如。 setImportsQ [...]并进行后续计算取决于该功能所修改的“设置”。因此,基本上,您希望共享多个计算的单子上下文。做到这一点的唯一方法是留在monad中-通过在Interpreter中构建单个计算并运行一次。您不能具有转义和重用runInterpreter的“全局变量”。

幸运的是,InterpreterMonadIO的实例,这意味着您可以使用IO交错Interpreter计算和liftIO :: IO a -> Interpreter a计算。基本上,您是由内而外的思考(对于Haskell的学习者来说,这是一个极为常见的错误)。不要在IO中使用在解释器中运行代码的函数,而应在Interpreter中使用在IO(即liftIO)中运行代码的函数。因此,例如。

main = runInterpreter $ do
    testHint
    expr1 <- liftIO getLine
    r1 <- interpret "" expr1
    case r1 of
        ...
    expr2 <- liftIO getLine
    r2 <- interpret "" expr2
    case r2 of
        ...

而且,如果需要的话,您可以使用参照透明的美感轻松地将后面的代码提取到函数中!只需将其拉出即可。
runSession :: Interpreter ()
runSession = do
    expr1 <- liftIO getLine
    r1 <- interpret "" expr1
    case interpret expr1 of
        ...

main = runInterpreter $ do
    testHint
    runSession

那有意义吗?您的整个程序都是Interpreter计算,只有在最后一刻,您才将其拉到IO中。

(这并不意味着您编写的每个函数都应该在Interpreter monad中。远离它!与往常一样,在程序边缘使用Interpreter并保持核心纯功能。Interpreter是新的IO)。

07-28 14:02
查看更多