如何在Haskell中创建结合状态和错误的monad

如何在Haskell中创建结合状态和错误的monad,haskell,Haskell,我正在尝试创建一个结合了状态和错误处理的monad,如下所示 import Control.Monad data Result a e = Ok a | Error e newtype StateError s e a = StateError { runStateError :: s -> (Result a e, s) } instance Monad (StateError s e) where return x = StateError $ \s -> (Ok x,

我正在尝试创建一个结合了状态和错误处理的monad,如下所示

import Control.Monad

data Result a e = Ok a | Error e

newtype StateError s e a = StateError { runStateError :: s -> (Result a e, s) }

instance Monad (StateError s e) where
  return x = StateError $ \s -> (Ok x, s)

  m >>= f = StateError $
    \s -> case runStateError m s of
            (Ok x, s') -> runStateError (f x) s'
            e -> e

get = StateError $ \s -> ((Ok s), s)

put s = StateError $ \_ -> ((Ok ()), s)

main = return ()
当我编译时,我收到这个错误,我不知道如何修复它:

StateError.hs:13:18: error:
    • Couldn't match type ‘a’ with ‘b’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          (>>=) :: forall a b.
                   StateError s e a -> (a -> StateError s e b) -> StateError s e b
        at StateError.hs:10:5-7
      ‘b’ is a rigid type variable bound by
        the type signature for:
          (>>=) :: forall a b.
                   StateError s e a -> (a -> StateError s e b) -> StateError s e b
        at StateError.hs:10:5-7
      Expected type: (Result b e, s)
        Actual type: (Result a e, s)
    • In the expression: e
      In a case alternative: e -> e
      In the expression:
        case runStateError m s of
          (Ok x, s') -> runStateError (f x) s'
          e -> e
    • Relevant bindings include
        e :: (Result a e, s) (bound at StateError.hs:13:13)
        f :: a -> StateError s e b (bound at StateError.hs:10:9)
        m :: StateError s e a (bound at StateError.hs:10:3)
        (>>=) :: StateError s e a
                 -> (a -> StateError s e b) -> StateError s e b
          (bound at StateError.hs:10:5)
   |
13 |             e -> e
   |                  ^
我做错了什么?我认为问题在于很难匹配
案例的两个结果

      Expected type: (Result b e, s)
      Actual type: (Result a e, s)
比如强制
a
变成
b
,或者类似的东西,但我不知道如何解决这个问题

此外,我还收到以下错误:

StateError.hs:7:10: error:
    • No instance for (Applicative (StateError s e))
        arising from the superclasses of an instance declaration
    • In the instance declaration for ‘Monad (StateError s e)’
  |
7 | instance Monad (StateError s e) where
  |          ^^^^^^^^^^^^^^^^^^^^^^
这要求我实例化
Applicative
,因此我希望能在这里得到一些指导


谢谢

您的错误已通过将代码更改为

  m >>= f = StateError $
    \s -> case runStateError m s of
            (Ok x, s1) -> runStateError (f x) s1
            (Error e, s1) -> (Error e, s1)
        -- or:
        --  (Error e, s1) -> (Error e, s)     -- also works
        -- not:
        --  e             -> e                -- this doesn't
以及明显的
函子
应用
实例

instance Functor .... where
  fmap = liftM

instance Applicative .... where
  (<*>) = ap
  pure = return
在箭头的左边和右边有不同的类型。因为它在错误消息中抱怨

  Expected type: (Result b e, s)
  Actual type: (Result a e, s)
当您使用变量时,箭头两侧的变量将相同。因此
e
重用相同的值,但
Error e
根据需要创建适当类型的新值。我们确实需要新的类型,正如
(>>=)
的签名所要求的:


关于应用程序,也必须定义它。您只需将您的
return
放在应用程序实例中的
pure
名称下,然后在那里添加
ab=apab
。然后在Monad中have
return=pure
。所以这就解决了这个问题。为什么不把
StateT
或者
ExceptT
State
结合起来呢?@MarkSeemann因为我是哈斯凯尔的新手,正试图创建他的第一个单子,现在还不想搞乱单子变形金刚。此外,我不确定monad transformer版本是否更易于编写或在性能方面更高效。这是一个很好的理由:)知道这些monad transformer的存在可能会有用,不过,以后再说。@MarkSeemann是的,Mark,我知道它们的存在,还有“comonads”、“free monad”和各种“costrangenads”,但一次只能做一件事;-)好的,你帮了很多忙。请参阅问题中我的更新代码。现在,您可以帮助我使用
fmap
定义来简化monad绑定操作符的实现吗?被迫定义一个函子,而不利用它来简化monad实现,似乎是没有用的。这两个代码看起来非常相似,所以我想还有一些事情要做。否则,函子需要什么?不看,你真的应该问新问题,如果你的问题有实质性的变化。我真的建议你展开你的编辑,然后问一个新问题。谢谢。哦,我考虑过了,但决定在这个问题上这样做,因为所有的东西都在一个地方。我是否仍应将此部分移至另一个新问题中,并在新问题中引用此问题?是的,您应回滚编辑,提出新的“后续”问题,并将此问题的链接作为背景。我现在就给你滚回去。:)完成后,您可以看到新问题。
  Expected type: (Result b e, s)
  Actual type: (Result a e, s)
Monad m => m a -> (a -> m b) -> m b
--          ^^                    ^^