我倾向于先写和看写Functor
实例。加倍如此,因为如果您使用LANGUAGE DeriveFunctor
编译指示,那么data Foo a = Foo a deriving ( Functor )
大部分时间都可以使用。
当您Applicative
可以比您的Monad
. 例如,这是一个Err
数据类型
data Err e a = Err [e] | Ok a deriving ( Functor )
instance Applicative (Err e) where
pure = Ok
Err es <*> Err es' = Err (es ++ es')
Err es <*> _ = Err es
_ <*> Err es = Err es
Ok f <*> Ok x = Ok (f x)
instance Monad (Err e) where
return = pure
Err es >>= _ = Err es
Ok a >>= f = f a
上面我按顺序定义了实例,Functor
单独来看Monad
,每个实例都是正确的。不幸的是,Applicative
andMonad
实例不对齐:ap
并且与is和(<*>)
明显不同。(>>)
(*>)
Err "hi" <*> Err "bye" == Err "hibye"
Err "hi" `ap` Err "bye" == Err "hi"
出于敏感性的目的,特别是一旦 Applicative/Monad 提案在每个人的手中,这些应该保持一致。如果您定义了instance Applicative (Err e) where { pure = return; (<*>) = ap }
,那么它们将对齐。
但是,最后,您可能能够仔细梳理差异,Applicative
以便Monad
它们以良性方式表现不同 - 例如具有更懒惰或更有效的Applicative
实例。这实际上发生得相当频繁,我觉得陪审团仍然对“良性”的含义以及您的实例应该在什么样的“观察”下保持一致。在 Facebook 的 Haxl 项目中,可能最普遍使用这种方法的Applicative
实例比实例更并行化Monad
,因此效率更高,但代价是一些相当严重的“未观察到的”副作用。
无论如何,如果它们不同,请记录下来。