scala - 为什么具有 Monad 实例的类型的半群不组合?
问题描述
我正在尝试围绕 Cats 中的 Semigroupals。以下是 Underscore 的“Scala with Cats”中的陈述。
cats.Semigroupal
是一个允许我们组合上下文的类型类
trait Semigroupal[F[_]] {
def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]
}
参数
fa
和fb
是相互独立的:我们可以在将它们传递给之前以任一顺序计算它们product
。这与 形成对比flatMap
,后者对其参数施加了严格的顺序。
所以基本上,我们也应该能够结合两个Either
上下文,但这似乎不起作用:
import cats.instances.either._
type ErrorOr[A] = Either[Vector[String], A]
Semigroupal[ErrorOr].product(Left(Vector("Error 1")), Left(Vector("Error 2")))
// res3: ErrorOr[Tuple2[Nothing, Nothing]] = Left(Vector("Error 1"))
如果 semigroupal 的 USP 是急切地执行独立操作,则必须在传递给之前对两者进行评估product
,但我们无法获得组合结果。
我们可能期望
product
应用于Either
累积错误而不是快速失败。同样,也许令人惊讶的是,我们发现product
实现了与flatMap
.
这不是与最初的前提相反,即有一种替代方法可以组合任何相同类型的上下文吗?
map
为了确保语义一致,Cats' Monad(它扩展了 Semigroupal)根据和提供了产品的标准定义flatMap
。
为什么要product
根据map
and来实现flatMap
?这里指的是什么语义?
那么,为什么要打扰 Semigroupal 呢?答案是我们可以创建有用的数据类型,这些数据类型具有 Semigroupal(和 Applicative)的实例,但没有 Monad。这使我们能够以不同的方式实现产品。
这甚至意味着什么?
不幸的是,这本书没有详细介绍这些前提!网上也找不到资源。谁能解释一下?TIA。
解决方案
所以基本上,我们也应该能够组合两个 Either 上下文,但这似乎不起作用:
它起作用了,你可以看到结果是一个有效的结果,它类型检查。
Semigrupal只是暗示给定 anF[A]
和 aF[B]
它会产生 anF[(A, B)]
它并不意味着它能够独立评估两者。它可能会,但也可能不会。与Monad相反,它确实暗示它需要在F[A]
之前进行评估,因为要评估F[B]
它需要A
这不是与原来的前提相反,即有一种替代方法可以组合任何相同类型的上下文吗?
并不是真正的不同方法Monad[F] <: Semigroupal[F]
,因为您始终可以调用product
任何Monad。用Semigroupal实现一个函数只是意味着它对更多类型开放,但它不会改变每种类型的行为。
为什么要用 map 和 flatMap 来实现 product?这里指的是什么语义?
TL;博士; 一致性:
// https://github.com/typelevel/cats/blob/54b3c2a06ff4b31f3c5f84692b1a8a3fbe5ad310/laws/src/main/scala/cats/laws/FlatMapLaws.scala#L18
def flatMapConsistentApply[A, B](fa: F[A], fab: F[A => B]): IsEq[F[B]] =
fab.ap(fa) <-> fab.flatMap(f => fa.map(f))
上述定律意味着,对于 anyF[A]
和 for any F[A => B]
,只要存在Monad (实际上是FlatMap),F
则fab.ap(fa)
与fab.flatMap(f => fa.map(f))
现在,为什么?多种原因:
- 最常见的是最不意外原则,如果我有一堆 any 并将它们传递给一个通用函数,无论它是否需要Monad或Applicative我都希望它快速失败,因为这是Either的行为。
- Liskov,假设我有两个函数
f
andg
,f
期望Applicative和Monad,如果g
在后台调用我希望调用两者返回相同的结果。g
f
- 任何Monad都必须是Applicative ,但是Either的累积Applicative版本需要 的Semigroup,而Monad实例不需要。
Left
这甚至意味着什么?
这意味着我们可以定义另一种类型(例如Validated),它只满足Applicative定律而不满足Monad定律,因此它可以实现的累积版本ap
额外的好处是,因为有一个类型是Monad但可以实现一个不需要排序的Applicative的情况很常见。猫的维护者创建了Parallel来代表它。
因此,您可以直接在Eithers上使用,而不是将您的Eithers转换为Validateds来组合它们。mapN
parMapN
推荐阅读
- django - 使用 json-schemas 验证 JSONField 中的多态数据
- javascript - 尝试导入错误:“getMoviesList”未从“./actions”导出;反应还原错误
- python - PyQt5 - 从 TableWidget 上的 QcomboBox 获取信息
- html - 使用 css 将 ul 和 span 对齐到封闭 div 的最右侧
- python - 如何使用 Pandas 或 to_csv 简单地为单行着色?
- filebeat - 如何解析filebeat中的event.original字段?
- node.js - 如何在 AppEngine 上运行的 NodeJS 中正确获取 Cloud Functions 的令牌?
- node.js - 为什么 .pipe 在这个 Nodejs google.drive.get 函数中没有触发?
- node.js - Angular、Express 和 http-proxy-middleware。如何访问远程服务器?
- python - 创建一个函数以在数据集中按名称搜索 Composer