如何在 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)
Most of this is straight from the hint example. What I'd like to do is initialize the interpreter with modules and imports and such and keep it around somehow. Later on (user events, or whatever), I want to be able to call a function with that initialized state and interpret an expression many times. So at the --split here location in the code, I want to have the code above in init, and the code below that in a new function that takes an expression and interprets it.
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)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我无法理解你的问题。另外我对提示也不是很熟悉。但我会尝试一下。
据我所知,
Interpreter
monad 只是一个围绕IO
的简单状态包装器——它的存在只是为了让你可以说例如。setImportsQ [...]
并让后续计算取决于该函数修改的“设置”。所以基本上你想共享多个计算的单子上下文。做到这一点的唯一方法是留在 monad 中——在 Interpreter 中构建单个计算并运行一次。您不能拥有转义并重用 runInterpreter 的“全局变量”。幸运的是,
Interpreter
是MonadIO
的一个实例,这意味着您可以使用交错
。基本上你是从内到外思考(对于 Haskell 学习者来说这是一个极其常见的错误)。不要使用IO
计算和Interpreter
计算>liftIO::IO a->解释器aIO
中在解释器中运行代码的函数,而是使用Interpreter
中在IO
中运行代码的函数(即liftIO )。所以例如。
如果需要的话,您可以轻松地将后面的代码提取到函数中,利用引用透明性的优点!直接拉出来就可以了
这有道理吗?您的整个程序是一个
解释器
计算,只有在最后一刻才将其拉出到IO
。(这并不意味着您编写的每个函数都应该在
Interpreter
monad 中。远非如此!像往常一样,在边缘使用Interpreter
您的程序并保持核心纯粹的功能。解释器
是新的IO
)。I'm having trouble understanding your question. Also I am not very familiar with hint. But I'll give it a go.
As far as I can tell, the
Interpreter
monad is just a simple state wrapper aroundIO
-- it only exists so that you can say eg.setImportsQ [...]
and have subsequent computations depend on the "settings" that were modified by that function. So basically you want to share the monadic context of multiple computations. The only way to do that is by staying within the monad -- by building a single computation inInterpreter
and running it once. You can't have a "global variable" that escapes and reusesrunInterpreter
.Fortunately,
Interpreter
is an instance ofMonadIO
, which means you can interleaveIO
computations andInterpreter
computations usingliftIO :: IO a -> Interpreter a
. Basically you are thinking inside-out (an extremely common mistake for learners of Haskell). Instead of using a function inIO
that runs code in your interpreter, use a function inInterpreter
that runs code inIO
(namelyliftIO
). So eg.And you can easily pull that latter code out into a function if you need to, using the beauty of referential transparency! Just pull it straight out.
Does that make sense? Your whole program is an
Interpreter
computation, and only at the last minute do you pull it out intoIO
.(That does not mean that every function you write should be in the
Interpreter
monad. Far from it! As usual, useInterpreter
around the edges of your program and keep the core purely functional.Interpreter
is the newIO
).如果我理解正确,您希望初始化编译器一次,然后运行多个查询(可能是交互式的)。
有两种主要方法:
IO
操作提升到Interpreter
上下文中(请参阅 luqui 的答案)。我将描述第二个选项。
借助惰性 IO 的魔力,您可以向
testHint
传递一个惰性输入流,然后在testHint
的主体中循环,以交互方式解释许多查询:go< /code> 函数可以访问初始化解释器的封闭环境,因此向其提供事件显然将在该一次初始化解释器的范围内运行。
另一种方法是从 monad 中提取解释器状态,但我不确定这在 GHC 中是否可行(它从根本上要求 GHC 不在 IO monad 中)。
If I understand correctly, you want to initialize the compiler once, and run multiple queries, possibly interactively.
There are two main approaches:
IO
actions into yourInterpreter
context (see luqui's answer).I'll describe the second option.
By the magic of lazy IO, you can pass
testHint
a lazy stream of input, then loop in the body oftestHint
, interpreting many queries interactively:The
go
function has access to the closed-over environment of the initialized interpreter, so feeding it events will obviously run in the scope of that once-initialized interpreter.An alternative method would be to extract the interpreter state from the monad, but I'm not sure that is possible in GHC (it would require GHC not to be in the
IO
monad fundamentally).