haskell - 无法将类型 ‘a’ 与 Monad 实例定义中的 ‘b’ 错误匹配

标签 haskell monads functor monad-transformers applicative

我正在编写一个 haskell 程序来执行一堆语句来修改数据记录。我想在没有用户干预的情况下对每个语句的状态进行修改和测试。我有修改 Control.Monad.Trans.State.Strict 的想法模块将我的特定类型合并到元组 (,) 中。

在出现以下错误之前,它运行良好

• Couldn't match type ‘a’ with ‘b’
  ‘a’ is a rigid type variable bound by
    the type signature for:
      (>>=) :: forall a b.
               StateT s m a -> (a -> StateT s m b) -> StateT s m b
    at TestMon.hs:38:7
  ‘b’ is a rigid type variable bound by
    the type signature for:
      (>>=) :: forall a b.
               StateT s m a -> (a -> StateT s m b) -> StateT s m b
    at TestMon.hs:38:7
  Expected type: m (b, s, LogS)
    Actual type: m (a, s, LogS)

而且我不明白为什么会出现此错误。 我的代码是这样的:

module TestStateJLJ1  where

import Data.Functor.Identity
import Control.Applicative
import Control.Monad

data LogS = LogOK { stepS:: Int ,logS::[String] }
          | LogErr {stepS:: Int , logS::[String] }
         deriving (Show)

initLogS = LogOK { stepS=1, logS=[]}

type State s = StateT s Identity

state :: (Monad m) => ((s,LogS) -> (a, s,LogS)) -> StateT s m a
state f  = StateT (return . f )

newtype StateT s m a = StateT { runStateT :: (s,LogS) -> m (a,s,LogS) }

instance (Functor m) => Functor (StateT s m) where
    fmap f m = StateT $ \ (s,l) ->
        fmap (\ (a, s',l') -> (f a, s',l')) $ runStateT m (s,l)

instance (Functor m, Monad m) => Applicative (StateT s m) where
    pure a = StateT $ \ (s,l) -> return (a, s,l)

    StateT mf <*> StateT mx = StateT $ \ (s,l) -> do
        (f, s',l') <- mf (s,l)
        (x, s'',l'') <- mx (s',l')
        return (f x, s'',l'')

    m *> k = m >>= \_ -> k

instance (Monad m) => Monad (StateT s m) where
    return a = StateT $ \ (s,l) -> return (a, s,l)

    m >>= k  = StateT $ \ (s,l) -> do
        (a, s',l') <- runStateT m (s,l)
        case l' of
           LogOK _ _ -> runStateT (k a) (s',l'{stepS=1+stepS l'})
           LogErr _ _-> do return ( a, s',l') -- <- This line is causing trouble

    fail str = StateT $ \ _ -> fail str

我试图修改 Monad 实例的行为来测试 LogS 数据类型的值并根据它的值:

  • 执行 stepS 的增量(计算语句的数量)和许多其他事情(尚未实现)并继续 monad 执行。

  • 或者停止 monad 执行并返回实际状态。

您知道我的代码有什么问题以及如何改正吗?

最佳答案

您需要从其他 monad 获取一个页面,这些 monad 在 monad 计算过程中发出错误信号。它们不会返回错误值,因为这通常是不可能的:m b 类型的单子(monad)操作只能 返回 b 类型的值,而不是像 (a,s,LogS) 这样的其他任意类型。相反,这些其他错误信号单子(monad)使用总和类型来表示错误。例如,monadic 操作Either MyNastyError b 只能返回 b 类型的值(通过使用Right) , 但它可以使用 Left 发出 MyNastyError 类型的错误。

此外,您通常无法生成 (a,s,LogS),因为对 a 类型的了解还不够。它只是在最终失败的操作之前立即绑定(bind)到整个 monadic 操作中的某些操作的返回类型,因此调用者通常不会准备好对其进行任何操作。不过,您可以返回 (s,LogS),因为该类型在给定 monad 中的所有操作中都是固定的。

具体来说,您可以将 StateT 类型重新定义为:

newtype StateT s m a
  = StateT { runStateT :: (s, LogS) -> m (Maybe a, s, LogS) }
  deriving (Functor)

这使用 Maybe a 来表示失败。返回 (Just x, s, l) 的计算可以继续,但是 (Nothing, s, l) 已准备好停止并转储其状态和日志。

对于这种类型,两个 LogS 构造函数现在是多余的,因为失败已经由 Maybe a 值发出信号,因此 LogS 可以简化为:

data LogS = LogS { stepS :: Int , logS :: [String] }
  deriving (Show)

适当的 ApplicativeMonad 实例可以更新为:

instance (Monad m) => Applicative (StateT s m) where
  pure a = StateT $ \(s, l) -> return (Just a, s, l)
  (<*>) = ap
instance (Monad m) => Monad (StateT s m) where
  return = pure
  m >>= k = StateT $ \(s, l) -> do
    (maybeA, s', l') <- runStateT m (s, l)
    case maybeA of
      Nothing -> return (Nothing, s', l')
      Just a -> runStateT (k a) (s', l' {stepS = 1 + stepS l'})
  fail err = StateT $ \(s, l) -> return (Nothing, s, l { logS = err:logS l })

请注意,我认为如果 fail 在此 monad 中执行“不错”的失败而不是使用基本 monad 的 fail 则更有意义。我在这里假设 logS 字段的顺序相反,因此最新的消息附加到头部。

完整代码:

{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE DeriveFunctor #-}

module TestStateJLJ1  where

import Data.Functor.Identity
import Control.Monad

data LogS = LogS { stepS :: Int , logS :: [String] }
  deriving (Show)

initLogS :: LogS
initLogS = LogS 1 []

newtype StateT s m a
  = StateT { runStateT :: (s, LogS) -> m (Maybe a, s, LogS) }
  deriving (Functor)

instance (Monad m) => Applicative (StateT s m) where
  pure a = StateT $ \(s, l) -> return (Just a, s, l)
  (<*>) = ap
instance (Monad m) => Monad (StateT s m) where
  return = pure
  m >>= k = StateT $ \(s, l) -> do
    (maybeA, s', l') <- runStateT m (s, l)
    case maybeA of
      Nothing -> return (Nothing, s', l')
      Just a -> runStateT (k a) (s', l' {stepS = 1 + stepS l'})
  fail err = StateT $ \(s, l) -> return (Nothing, s, l { logS = err:logS l })

type State s = StateT s Identity

state :: (Monad m) => ((s, LogS) -> (Maybe a, s, LogS)) -> StateT s m a
state f = StateT $ return . f

关于haskell - 无法将类型 ‘a’ 与 Monad 实例定义中的 ‘b’ 错误匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59354774/

相关文章:

c++ - 仿函数与模板参数

c++ - 在 C++ 中返回仿函数的正确方法

haskell - 存在类型的类型变量引入

haskell - 向 Monad 结果类型添加约束

c - hscurses 或 ncurses,使用哪一个?

Scalaz 迭代器 : "Lifting" `EnumeratorT` to match `IterateeT` for a "bigger" monad

scala - Scalaz 是否在错误和成功方面都可以积累一些东西?

c++ - 如何用仿函数定义 vector

postgresql - Esqueleto `selectDistinct` 不工作

haskell - Haskell 中的堆栈