为什么验证不是一个Monad?

29

一个使用案例的例子:

def div2(i: Int): Validation[String, Int] = 
    if (i%2 == 0) Validation.success(i/2)
    else Validation.failure("odd")

def div4(i: Int) = for {
    a <- div2(i)
    b <- div2(a)
} yield b

错误: 无法将类型scalaz.Validation[String,Int]取消应用到类别为M[_]的类型构造器,该类型构造器由类型类scalaz.Bind所分类。

猜测这个错误是由于编译器找不到Validation[String, Int]Monad实例导致的。

我可以自己制作一个,例如:

object Instances {
implicit def validationMonad[E] = new Monad[({type L[A] = Validation[E, A]})#L] {
    override def point[A](a: => A) =
        Validation.success(a)
    override def bind[A, B](fa: Validation[E, A])(f: A => Validation[E, B]) =
        fa bind f
}
}
但是为什么Validation没有它已经定义了bind方法的这个功能呢?
此外,我不能再同时拥有import Validation._import Instances._(这让我花了很长时间才找到...),因为另一个复杂的错误...存在歧义的隐式值:例如,我的实例validationMonad和特质ValidationInstances2中的方法ValidationInstances1都匹配Functor of Validation...
我应该修改scalaz的源代码吗?还是我完全错过了什么?
请帮帮我,我使用的是scalaz 7.0.0-M2。
2个回答

23
如在Scalaz组中讨论的那样,问题似乎是ap会积累错误,而(伪)单子合成仅对Validation的值部分进行操作。
因此,一个不能用另一个来表示,因此对于Validation不存在单子实例。

1
嗯...也许我在函数式编程方面不太好,我仍然无法理解为什么(在讨论中)该属性应该保持不变...但我想这已经回答了我的问题,谢谢~ - Chris
2
但是作为一个附带问题,除了自己定义 'Monad' 实例,我的用例中没有别的出路吗?我的方法有什么不好的地方吗? - Chris
3
如果您不关心自动追加失败的操作,那么一个明显的选择是完全切换到 scalaz.\/(Scalaz 中替代 Either 的右偏向类型)它具有您所需的语义,因此对其他人来说可能更容易理解。(实现一条基本上违反了单子律和应用律的规则可能完全运作良好且可预测,但仍可能会有些混淆。) - Debilski
@chris 我会说你的方法只有一件微小的问题,即某些人可能会感到惊讶的是单子定律被违反了。但是,根据我的直觉,在这种情况下违反单子定律实际上是完全合理的。 - Owen

6
问题在于,由单子所隐含的应用函子并不等同于实际的应用函子。

9
你能否进一步解释下什么是隐式(implied)的,什么是实际的(actual)?我对这些类型类的东西不太了解。 - Chris
2
尊敬的提醒,@oxbow_lakes - Kevin Meredith
1
你可以合法地从单子绑定/链中派生出应用接口(它只是一个链操作,以获取内部值(一个函数),然后使用该函数映射第二个应用)。但是,如果你实际上实现了这个操作,你最终会得到一个你期望从Either类型中得到的应用行为,而不是从Validation中得到的行为。 - Dtipson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接