大部分内容都来自提示示例。我想做的是用模块和导入等初始化解释器并以某种方式保持它。稍后(用户事件或其他),我希望能够调用具有该初始化状态的函数并多次解释表达式。所以在代码中的--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)
答案 0 :(得分:3)
如果我理解正确,您希望初始化编译器一次,并可能以交互方式运行多个查询。
主要有两种方法:
IO
行动提升到您的Interpreter
背景中(请参阅luqui的回答)。我将描述第二种选择。
通过懒惰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不要从根本上处于IO
monad)。
答案 1 :(得分:3)
我无法理解你的问题。另外我对提示不是很熟悉。但我会试一试。
据我所知,Interpreter
monad只是一个简单的状态包装器IO
- 它只存在,所以你可以说例如。 setImportsQ [...]
并且后续计算取决于该函数修改的“设置”。所以基本上你想要分享多个计算的monadic上下文。唯一的方法是留在monad中 - 在Interpreter
中构建一个计算并运行一次。你不能拥有一个逃避并重用runInterpreter
的“全局变量”。
幸运的是,Interpreter
是MonadIO
的一个实例,这意味着您可以使用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
)。