Monad实例定义中无法将类型“ a”与“ b”错误匹配

时间:2019-12-16 10:31:06

标签: 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并返回实际状态。

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

1 个答案:

答案 0 :(得分:1)

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

此外,由于对类型Left的了解不足,因此您通常将无法产生(a,s,LogS)。只是某些操作的返回类型会在操作最终失败之前立即绑定到整体monadic操作中,因此调用方通常不会准备对其执行任何操作。不过,您可以返回a,因为该类型将在给定monad的所有操作中固定。

具体来说,您可以将(s,LogS)类型重新定义为:

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) Applicative实例可以更新为:

Monad

请注意,如果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 }) 在此monad中确实发生了“不错”的故障,而不是使用基本monad的fail,则更有意义。我在这里假设fail字段的顺序相反,因此最新消息会附加到头部。

完整代码:

logS