嵌套do块中令人困惑的类型失误错误

发布于 2025-02-05 19:20:53 字数 1956 浏览 3 评论 0原文

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

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

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

结果类型之所以这样,是因为它是状态的,因此eval应该能够做到io,并且可能会失败。

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

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 (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(要么要么...)]下一个Do应该将我放在IO Monad中(因为这是结果类型中的下一个内部类型),因此我应该能够提取IO值,据我了解, sequence elems应该给我一个io [...],我可以提取,那么我的错误是什么?

I'm trying to write an interpreter for a simple embedded scripting language.

The core of it is the eval function, which has the following signature.

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

The result type is like this because it is statefull, eval should be able todo IO and it can fail.

There are two datatypes: Expressions and Values and eval converts expression to values. For simple expressions, literals of primitive data types, it's implemented like this:

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)

My problem now is implementing eval for the list literal. Currently my code looks like this:

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

And this produces the following error:

/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
   |             ^^^^^^^^^^^^^^

My problem is that I'm not understanding this error. My thinking goes like this: the first do puts me in the State monad, so I shold be able to "extract" the results from mapM eval elems which I expect to be [ IO (Either ...) ] The next do should put me in the IO monad (because that's the next inner type in the result type), so I should be able to extract IO values, and as far as I understood it, sequence elems should give me a IO [ Either ... ] which I can then extract, so what is my mistake?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

习惯成性 2025-02-12 19:20:54

一般而言,单子不构成。我认为您对此感到厌烦。

通常,如果mn是单子,我们将无法编写m(na)用于混合m 和n。总的来说,它甚至可能失败了。

在您的情况下,您正在使用状态A(io b)希望能够访问类型a的状态,并且仍然做io,但事实并非如此。

实际上,根据定义,我们有(最多包装器):

State a b = a -> (a,b)
            |     | |-- result
            |     |-- new state
            |-- old 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完成后生成新状态一样。

要获得该类型,您无法嵌套单子,但是您需要一个单调的变形金刚 statet

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

a monad。您可能需要使用liftliftio将IO操作转换为此类类型,但应按需要行为。

Monad do not compose, in general. I think you are being bit by this.

In general, if m and n are monads, we can not write m (n a) for a monadic action that mixes the features of m and n. In general, it could even fail to be a monad.

In your case, you are using something like State A (IO B) hoping to be able to access the state of type A and still do IO, but that's not the case.

Indeed, by definition we have (up to some wrappers):

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

which in your case State A (IO B) it becomes

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

Here, we can see that the new state must be generated without doing IO at all! This is a computation which forces the new state and the IO effects to be completely separated. It effectively behaves as if we had two separate functions

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

Likely, that's not what you actually want. That is probably something like

A -> IO (A, B)

allowing the new state to be generated after IO is done.

To obtain that type, you can not nest monads, but you need a monad transformer like StateT:

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

This is a monad. You will probably need to use lift or liftIO to convert IO actions to this type, but it should behave as desired.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文