违反关联性法则的monad会产生不正确的理解力吗? [英] Can monad with broken associativity law yield incorrect result in for-comprehension?
问题描述
这是ListT
的Monad
实例(从 montrivo 复制)
case class ListT[M[_], A](value: M[List[A]])
implicit def listTMonad[M[_]: Monad] = new Monad[ListT[M, *]] {
override def flatMap[A, B](fa: ListT[M, A])(f: A => ListT[M, B]): ListT[M, B] =
ListT(
Monad[M].flatMap[List[A], List[B]](fa.value)(
list => Traverse[List].flatTraverse[M, A, B](list)(a => f(a).value)
)
)
override def pure[A](a: A): ListT[M, A] = ListT(Monad[M].pure(List(a)))
override def tailRecM[A, B](a: A)(f: A => ListT[M, Either[A, B]]): ListT[M, B] = ???
}
它确实不满足 因为,尽管我们获得了相同的值,但它们的顺序却不同 但是,在理解方面(在我的个人项目中),它似乎可以正常工作.通常,在理解中使用会破坏关联性法则的单子"安全吗?您能提供一个反例来证明错误的结果吗? 由于 然后: 这是您的示例,改写为直接使用 作为旁注,我不确定是否安全?"正是表达这个问题的最佳方法.如果您在 合法性为您带来自信地执行某些类型的重写的能力,并且一眼就能知道表达式具有相同的值(例如 Here is a It does not satisfy associativity monad law because, although we get the same values, they are in different order However it seem to work correctly in for-comprehensions (in my personal project). Generally, is it safe to use "monads" that brake associativity law in for-comprehensions? Could you provide a counter-example demonstrating incorrect result? Since And then: This is your example rewritten to use As a side note, I'm not sure "is it safe?" is exactly the best way to phrase this question. If your What lawfulness gives you is the ability to perform certain kinds of rewriting with confidence, and to be able to know at a glance that expressions have the same value (e.g. 这篇关于违反关联性法则的monad会产生不正确的理解力吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!ListT(List(List(0, 1, 0, 0, 1), List(0, 1, 1, 0, 1), List(0, 1, 0, 0), List(0, 1, 0, 1), List(0, 1, 1, 0), List(0, 1, 1, 1)))
ListT(List(List(0, 1, 0, 0, 1), List(0, 1, 0, 0), List(0, 1, 0, 1), List(0, 1, 1, 0, 1), List(0, 1, 1, 0), List(0, 1, 1, 1)))
for
-理解是flatMap
(和map
)的语法糖,因此肯定会导致flatMap
损坏的情况错误的for
-综合代码.例如:import cats.{Monad, Traverse}, cats.implicits._
// Your code here...
val first = for {
y <- for {
x <- a(0)
y <- a(x)
} yield y
z <- a(y)
} yield z
val second = for {
x <- a(0)
y <- a(x)
z <- a(y)
} yield z
scala> first == second
res0: Boolean = false
for
-comprehensions而不是flatMap
(这里最后还有一些map
额外的操作,但这只是实现细节,并不真正相关).ListT
中的for
理解产生了正确的结果—即使ListT
的flatMap
不具有关联性,它们也肯定会产生正确的结果–那么从某种意义上说它们是安全的". >
a(0).flatMap(a).flatMap(a)
和a(0).flatMap(a(_).flatMap(a))
),而不必考虑他们使用的方法的实现.这是您所缺少的,因为ListT
没有关联的flatMap
.判断是否安全"是您必须做出的判断.Monad
instance for ListT
(copied from montrivo)case class ListT[M[_], A](value: M[List[A]])
implicit def listTMonad[M[_]: Monad] = new Monad[ListT[M, *]] {
override def flatMap[A, B](fa: ListT[M, A])(f: A => ListT[M, B]): ListT[M, B] =
ListT(
Monad[M].flatMap[List[A], List[B]](fa.value)(
list => Traverse[List].flatTraverse[M, A, B](list)(a => f(a).value)
)
)
override def pure[A](a: A): ListT[M, A] = ListT(Monad[M].pure(List(a)))
override def tailRecM[A, B](a: A)(f: A => ListT[M, Either[A, B]]): ListT[M, B] = ???
}
val a: Int => ListT[List, Int] = {
case 0 => ListT(List(List(0, 1)))
case 1 => ListT(List(List(0), List(1)))
}
assert(a(0).flatMap(a).flatMap(a) != a(0).flatMap(x ⇒ a(x).flatMap(a)), "Associativity law is not satisfied")
ListT(List(List(0, 1, 0, 0, 1), List(0, 1, 1, 0, 1), List(0, 1, 0, 0), List(0, 1, 0, 1), List(0, 1, 1, 0), List(0, 1, 1, 1)))
ListT(List(List(0, 1, 0, 0, 1), List(0, 1, 0, 0), List(0, 1, 0, 1), List(0, 1, 1, 0, 1), List(0, 1, 1, 0), List(0, 1, 1, 1)))
for
-comprehensions are syntactic sugar for flatMap
(and map
), it definitely is the case that a broken flatMap
could result in incorrect for
-comprehension code. For example:import cats.{Monad, Traverse}, cats.implicits._
// Your code here...
val first = for {
y <- for {
x <- a(0)
y <- a(x)
} yield y
z <- a(y)
} yield z
val second = for {
x <- a(0)
y <- a(x)
z <- a(y)
} yield z
scala> first == second
res0: Boolean = false
for
-comprehensions instead of flatMap
directly (there are also some extra map
operations at the end here, but that's an implementation detail and not really relevant).for
-comprehensions in ListT
produce the correct result—and they definitely might, even though ListT
's flatMap
isn't associative—then in a sense they're "safe".a(0).flatMap(a).flatMap(a)
and a(0).flatMap(a(_).flatMap(a))
), without having to look into the implementations of the methods they use. This is what you're missing because ListT
doesn't have an associative flatMap
. Whether that counts as "safe" or not is a judgment call you have to make.