在Haskell中如何初始化monad,然后在函数中多次使用

|| 大多数情况直接来自提示示例。我想做的是使用模块和导入等初始化解释器,并以某种方式保留它。稍后(用户事件或其他事件),我希望能够以该初始化状态调用一个函数并多次解释一个表达式。因此,在代码的--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只是一个围绕ѭ2simple的简单状态包装器-它仅存在,以便您可以说例如。
setImportsQ [...]
并进行后续计算取决于该功能所修改的“设置”。因此,基本上,您希望共享多个计算的单子上下文。做到这一点的唯一方法是留在monad中-在
Interpreter
中构建单个计算并运行一次。您不能使用\“全局变量\”来转义并重复使用
runInterpreter
。 幸运的是,
Interpreter
MonadIO
的一个实例,这意味着您可以使用
liftIO :: IO a -> Interpreter a
交织
IO
计算和ѭ1and计算。基本上,您是由内而外的思考(对于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
)。
如果我理解正确,则希望一次初始化编译器,并可能以交互方式运行多个查询。 主要有两种方法: 将
IO
动作提升到
Interpreter
上下文中(请参阅luqui的答案)。 使用懒惰的IO将数据流走私到您的程序中。 我将描述第二个选项。 借助惰性IO的魔力,您可以传递
testHint
惰性输入流,然后循环进入
testHint
的正文,以交互方式解释许多查询:
main = do
      ls <- getContents   -- a stream of future input
      r <- runInterpreter (testHint (lines input))
      case r of
         Left err -> printInterpreterError err
         Right () -> putStrLn \"Done.\"

testHint input = do
      loadModules [\"src/Test/SomeModule.hs\"]
      setImportsQ [(\"Prelude\", Nothing), (\"Test.SomeModule\", Just \"SM\")]
      say \"loaded\"

      -- loop over the stream of input, interpreting commands
      let go (\":quit\":es) = return ()
             (e:es)       = do say =<< typeOf e
                               go es
      go
go
函数可以访问已初始化解释器的封闭环境,因此,将事件馈入显然会在该已初始化解释器的范围内运行。 一种替代方法是从monad中提取解释器状态,但我不确定在GHC中是否可能(这将要求GHC基本上不处于the2ѭmonad中)。

要回复问题请先登录注册