正如我在评论中提到的,至少有两种累积错误的方法。下面我详细阐述一下这些。我们需要这些导入:
import Control.Applicative
import Data.Monoid
import Data.These
TheseT
单子变压器
免责声明: TheseT
叫做ChronicleT in these package.
看看下面的定义These数据类型:
data These a b = This a | That b | These a b
Here This
and That
相当于Left
and Right
of Either
数据类型。These
数据构造函数能够积累能力Monad
实例:它包含两个结果(类型为b
)和以前的错误的集合(类型的集合a
).
利用现有的定义These
我们可以轻松创建的数据类型ErrorT
-类似单子变压器:
newtype TheseT e m a = TheseT {
runTheseT :: m (These e a)
}
TheseT
是一个实例Monad
通过以下方式:
instance Functor m => Functor (TheseT e m) where
fmap f (TheseT m) = TheseT (fmap (fmap f) m)
instance (Monoid e, Applicative m) => Applicative (TheseT e m) where
pure x = TheseT (pure (pure x))
TheseT f <*> TheseT x = TheseT (liftA2 (<*>) f x)
instance (Monoid e, Monad m) => Monad (TheseT e m) where
return x = TheseT (return (return x))
m >>= f = TheseT $ do
t <- runTheseT m
case t of
This e -> return (This e)
That x -> runTheseT (f x)
These _ x -> do
t' <- runTheseT (f x)
return (t >> t') -- this is where errors get concatenated
Applicative
积累ErrorT
免责声明:这种方法更容易适应,因为您已经在m (Either e a)
newtype 包装器,但它仅适用于Applicative
环境。
如果实际代码只使用Applicative
我们可以摆脱的界面ErrorT
改变它的Applicative
实例。
让我们从非变压器版本开始:
data Accum e a = ALeft e | ARight a
instance Functor (Accum e) where
fmap f (ARight x) = ARight (f x)
fmap _ (ALeft e) = ALeft e
instance Monoid e => Applicative (Accum e) where
pure = ARight
ARight f <*> ARight x = ARight (f x)
ALeft e <*> ALeft e' = ALeft (e <> e')
ALeft e <*> _ = ALeft e
_ <*> ALeft e = ALeft e
定义时请注意<*>
we know如果双方都是ALeft
s,因此可以执行<>
。如果我们尝试定义相应的Monad
我们失败的例子:
instance Monoid e => Monad (Accum e) where
return = ARight
ALeft e >>= f = -- we can't apply f
所以唯一的Monad
我们可能有的例子是Either
。但是之后ap
不等于<*>
:
Left a <*> Left b ≡ Left (a <> b)
Left a `ap` Left b ≡ Left a
所以我们只能使用Accum
as Applicative
.
现在我们可以定义Applicative
变压器基于Accum
:
newtype AccErrorT e m a = AccErrorT {
runAccErrorT :: m (Accum e a)
}
instance (Functor m) => Functor (AccErrorT e m) where
fmap f (AccErrorT m) = AccErrorT (fmap (fmap f) m)
instance (Monoid e, Applicative m) => Applicative (AccErrorT e m) where
pure x = AccErrorT (pure (pure x))
AccErrorT f <*> AccErrorT x = AccErrorT (liftA2 (<*>) f x)
注意AccErrorT e m
本质上是Compose m (Accum e)
.
EDIT:
AccError
被称为AccValidation in validation package.