本文介绍了为什么具有 Monad 实例的类型的 Semigroupal 不组合?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试围绕 Cats 中的 Semigroupals 进行研究.以下是Scala with Cats"中的陈述:下划线.

cats.Semigroupal 是一个允许我们组合上下文的类型类

trait Semigroupal[F[_]] {def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]}

参数fafb 相互独立:我们可以在将它们传递给product 之前以任一顺序计算它们.这与 flatMap 形成对比,后者对其参数施加了严格的顺序.

所以基本上,我们也应该能够组合两个 Either 上下文,但这似乎不起作用:

导入cats.instances.either._类型 ErrorOr[A] = 任何一个[向量[字符串],A]半群[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 相同的快速失败行为.

是否有悖于原来的前提,即有一种替代方法能够组合任何相同类型的上下文?

为了确保语义一致,Cats 的 Monad(扩展了 Semigroupal)根据 mapflatMap 提供了产品的标准定义.

为什么用mapflatMap来实现product?这里指的是什么语义?

那么为什么要为半群而烦恼呢?答案是我们可以创建有用的数据类型,这些数据类型具有 Semigroupal(和 Applicative)的实例,但没有 Monad.这使我们能够以不同的方式实施产品.

这到底是什么意思?

不幸的是,本书没有详细介绍这些前提!网上也找不到资源.有人可以解释一下吗?TIA.

解决方案

它起作用了,正如你所看到的,结果是一个有效的结果,它进行了类型检查.

Semigrupal 只是暗示给定一个 F[A] 和一个 F[B] 它会产生一个 F[(A, B)] 这并不意味着它能够独立评估两者;可能会,但也可能不会.与 Monad 相反,它确实意味着它需要先评估 F[A],因为要评估 F[B] 它需要 一个

是否有悖于原来的前提,即有一种替代方法能够组合任何相同类型的上下文?

Monad[F] 以来并不是真正不同的方法,你总是可以在任何 Monadproduct>.以半群形式实现一个函数只是意味着它对更多类型开放,但不会改变每种类型的行为.

为什么用map和flatMap来实现product?这里指的是什么语义?

TL;博士;一致性:

//https://github.com/typelevel/cats/blob/54b3c2a06ff4b31f3c5f84692b1a8a3fbe5ad310/laws/src/main/scala/cats/laws/FlatMapLaws.s.L18def flatMapConsistentApply[A, B](fa: F[A], fab: F[A => B]): IsEq[F[B]] =fab.ap(fa) <->fab.flatMap(f => fa.map(f))

上述定律意味着对于任何 F[A] 和任何 F[A =>;B] 只要存在 Monad (实际上是 FlatMap) 对于 F 那么,fab.ap(fa)fab.flatMap(f => fa.map(f))

相同

现在,为什么?多种原因:

  • 最常见的是最小惊喜原则,如果我有一堆任一个,我将它们传递给一个泛型函数,无论它是需要Monad还是Applicativestrong> 我希望它很快失败,因为这是 Either 的行为.
  • Liskov,假设我有两个函数 fgf 需要一个 Applicativeg 一个 Monad,如果 g 在幕后调用 f,我希望两者都调用返回相同的结果.
  • 任何Monad都必须是Applicative,但是Either的累积Applicative版本需要Left 的 Semigroup,而 Monad 实例不需要.

这到底是什么意思?

这意味着我们可以定义另一种类型(例如,已验证) 只满足 Applicative 定律而不满足 Monad 定律,因此它可以实现 Applicative 定律的累积版本代码>ap


额外的好处,因为拥有类型是 Monad 但可以实现不需要排序的 Applicative 的情况非常普遍.cats 维护者创建了Parallel 代表那个.

因此,不是将您的 Eithers 转换为 Validated 以使用 mapN 将它们组合起来,您可以直接使用 parMapN要么.

I am trying wrap my head around Semigroupals in Cats. Following are statements from "Scala with Cats" by Underscore.

trait Semigroupal[F[_]] {
  def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]
}

So basically, we should be able to combine two Either contexts as well but that doesn't seem to work:

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"))

If the USP of semigroupal is to eagerly execute independent operations, both eithers must be evaluated before being passed to product and yet we can't have a combined result.

Isn't it contrary to the original premise of having an alternative approach to be able to combine any contexts of same type?

Why implement product in terms of map and flatMap? What semantics are being referred to here?

What does this even mean?

Unfortunately, the book doesn't covers these premises in detail! Neither can I find resources online. Could anyone please explain this? TIA.

解决方案

It worked, as you can see the result is a valid result, it type checks.

Semigrupal just implies that given an F[A] and a F[B] it produces an F[(A, B)] it doesn't imply that it would be able to evaluate both independently or not; it may, but it may as well not. Contrary to Monad which does imply that it needs to evaluate F[A] before because to evaluate F[B] it needs the A

Is not really a different approach since Monad[F] <: Semigroupal[F], you can always call product on any Monad. Implementing a function in terms of Semigroupal just means that it is open to more types, but it doesn't change the behavior of each type.

TL;DR; consistency:

// 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))

The above laws implies that for any F[A] and for any F[A => B] as long as there exists a Monad (actually FlatMap) for F then, fab.ap(fa) is the same as fab.flatMap(f => fa.map(f))

Now, why? Multiple reasons:

  • The most common one is the principle of least surprise, if I have a bunch of eithers and I pass them to a generic function, no matter if it requires Monad or Applicative I expect it to fail fast, since that is the behavior of Either.
  • Liskov, suppose I have two functions f and g, f expects an Applicative and g a Monad, if g calls f under the hood I would expect calling both to return the same result.
  • Any Monad must be an Applicative, however an accumulating Applicative version of Either requires a Semigroup for the Left, whereas, the Monad instance doesn't require that.

It means that we may define another type (for example, Validated) that would only satisfy the Applicative laws but not the Monad laws, as such it can implement an accumulating version of ap


Bonus, since having this situation of having a type that is a Monad but could implement an Applicative that doesn't require sequencing, is so common. The cats maintainers created Parallel to represent that.

So instead of converting your Eithers into Validateds to combine them using mapN you can just use parMapN directly on the Eithers.

这篇关于为什么具有 Monad 实例的类型的 Semigroupal 不组合?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-26 15:18