嵌套do块中出现混淆类型不匹配错误



我正试图为一种简单的嵌入式脚本语言编写一个解释器。

它的核心是eval函数,它具有以下签名。

type EvalState = () --for later
type EvalResult = State EvalState (IO (Either T.Text Value))
eval :: Expr -> EvalResult

结果类型是这样的,因为它是状态满的,eval应该能够执行IO,并且它可能会失败。

有两种数据类型:表达式和值,eval将表达式转换为值。对于简单的表达式,即原始数据类型的文字,它是这样实现的:

ok :: Value -> EvalResult
ok val = state (return . Right $ val,)
eval :: Expr -> EvalResult
eval (StrLit t) = ok (StrVal t)
eval (SymLit t) = ok (SymbolVal t)
eval (IntLit i) = ok (IntVal i)
eval (FloatLit f) = ok (FloatVal f)

我现在的问题是为列表文字实现eval。目前我的代码如下:

eval (ListLit elems) = do
elems <- mapM eval elems
do 
opts <- sequence elems
return $ fmap (Right . ListVal . V.fromList) (sequence opts)

这会产生以下错误:

/home/felix/git/vmail/src/Interpreter/EvalAst.hs:37:13: error:
• Couldn't match type ‘IO’
with ‘StateT EvalState Data.Functor.Identity.Identity’
Expected: StateT
EvalState Data.Functor.Identity.Identity [Either T.Text Value]
Actual: IO [Either T.Text Value]
• In a stmt of a 'do' block: opts <- sequence elems
In a stmt of a 'do' block:
do opts <- sequence elems
fmap (Right . ListVal . V.fromList) (sequence opts)
In the expression:
do elems <- mapM eval elems
do opts <- sequence elems
fmap (Right . ListVal . V.fromList) (sequence opts)
|
37 |     opts <- sequence elems
|             ^^^^^^^^^^^^^^

我的问题是我不理解这个错误。我的想法是这样的:第一次做会让我进入州立大学,所以我应该能够"提取物";mapM eval elems的结果,我希望是[ IO (Either ...) ]。下一步应该把我放在IO monad中(因为这是结果类型中的下一个内部类型),所以我应该能够提取IO值,据我所知,sequence elems应该给我一个IO [ Either ... ],然后我可以提取,那么我的错误是什么?

一般情况下,莫纳德不作曲。我想你被这件事折磨了。

一般来说,如果mn是单元的,我们就不能为混合了mn特征的单元动作编写m (n a)。总的来说,它甚至可能不能成为一个monad。

在您的情况下,您正在使用类似State A (IO B)的东西,希望能够访问类型为A的状态,并且仍然执行IO,但事实并非如此。

事实上,根据定义,我们有(多达一些包装):

State a b = a -> (a,b)
|     | |-- result
|     |-- new state
|-- old state

在你的情况下State A (IO B)变成

State A (IO B) = A -> (A, IO B)
|     |  |-- result
|     |-- new state
|-- old state

在这里,我们可以看到,必须在根本不做IO的情况下生成新状态!这是一种迫使新状态和IO效应完全分离的计算。它的有效行为就像我们有两个独立的功能

A -> A     -- state update, no IO here
A -> IO B  -- IO depending on the old state

很可能,这不是你真正想要的。这可能有点像

A -> IO (A, B)

从而允许在IO完成之后生成新状态。

要获得该类型,您不能嵌套monad,但您需要一个monad transformer,如StateT:

StateT A IO B = A -> IO (A, B)

这个一个monad。您可能需要使用liftliftIO将IO操作转换为此类型,但它的行为应该符合需要。

最新更新