为什么AccValidation没有Monad实例?

Chr*_*tin 7 monads haskell applicative

validation包的文档:

AccValidation数据类型是同构的Either,但有一个实例的Applicative,关于错误侧积聚.也就是说,如果遇到两个(或更多)错误,则使用Semigroup操作附加它们.

作为该Applicative实例的结果,没有对应BindMonad实例.AccValidation是一个"不是monad的应用函子"的例子.

我不清楚为什么会这样.我可以想象一个Monad例子的AccValidation行为就像Either- 什么会使这个非法?

dup*_*ode 9

(<*>) = apexigence可以明确的条款规定(>>=):

u <*> v = u >>= \f -> fmap f v -- [1]
Run Code Online (Sandbox Code Playgroud)

现在,考虑到FunctorApplicative实例AccValidation,我们有:

fmap _ (AccFailure e) = AccFailure e -- [2]

AccFailure e1 <*> AccFailure e2 = AccFailure (e1 <> e2) -- [3]
Run Code Online (Sandbox Code Playgroud)

如果我们u = AccFailure e1v = AccFailure e2[1],我们得到:

AccFailure e1 <*> AccFailure e2 = AccFailure e1 >>= \f -> fmap f (AccFailure e2)
Run Code Online (Sandbox Code Playgroud)

将[2]和[3]代入其中会导致我们:

AccFailure (e1 <> e2) = AccFailure e1 >>= \_ -> AccFailure e2 -- [4]
Run Code Online (Sandbox Code Playgroud)

问题是不可能写出(>>=)[4]成立的东西.左侧取决于在e2右侧必须来自应用的值\_ -> AccFailure e2 :: Semigroup e => a -> AccValidation e b.但是,没有什么可以应用它 - 特别是e1有错误的类型.(有关这一点的进一步讨论,请参阅Cactus答案的最后两段.)因此,没有办法给出AccValidation一个Monad与其一致的实例Applicative.


Cac*_*tus 6

机械地,Either-ish Monad实例AccValidation将是

-- The (Monoid err) context is not used for anything, 
-- it's just there to satisfy the Applicative super-instance
instance (Monoid err) => Monad (AccValidation err) where
    return = AccSuccess
    AccFailure err >>= f = AccFailure err
    AccSuccess x >>= f = f x
Run Code Online (Sandbox Code Playgroud)

这意味着我们有

AccFailure err1 <*> AccFailure err2 = AccFailure (err1 <> err2)
AccFailure err1 `ap` AccFailure err2 = AccFailure err1
Run Code Online (Sandbox Code Playgroud)

打破了monad法则<*> = ap.

直观地说,它不能成为monad,因为在monad中,计算的效果(即验证失败)可能取决于先前绑定的结果.但是在失败的情况下,没有结果.因此Either,在这种情况下,除了短路到故障之外别无选择,因为没有任何东西可以用于(>>=)s的右侧的后续功能.

这与应用仿函数形成鲜明对比,其中效果(在这种情况下,验证失败)不能依赖于其他结果,这就是为什么我们可以获得所有验证失败而不必提供结果(它们来自哪里?)来自一个计算到另一个.

  • @notgiorgi不完全是。[`ap`是使用(Monad`)方法实现的((*))](https://hackage.haskell.org/package/base-4.9.1.0/docs/src/GHC.Base.html#ap )。ap代表(&lt;*&gt;),liftM代表fmap。 (3认同)