使用cats.Semigroup可以这样写:
import cats.Semigroup
import cats.implicits._
val l1: String Either Int = Left("error")
val r1: String Either Int = Right(1)
val r2: String Either Int = Right(2)
l1 |+| r1 // Left("error")
r1 |+| r2 // Right(3)
我想要一个同样惯用的运算符(类似组合),其工作原理如下:
- 如果我的计算中有(至少)一个
Right
,则返回一个Right
- 如果仅
Left
,则返回Left
例如:
Right(1) |+| Right(2) // Right(3)
Right(1) |+| Left("2") // Right(1)
Left("1") |+| Left("2") // Left("12") // in my particular case the wrapped value here does not really matter (could also be e.g. Left("1") or Left("2")), but I guess Left("12") would be the must logical result
是否已经在例如中定义了类似的内容?猫要么
?
最佳答案
Either
有一堆合法半群实例,其中哪些应该包含在《猫》中是 some 的问题。 debate 。 Cats、Scalaz 和 Haskell 在这方面都做出了不同的选择,而你所描述的实例(翻转但左右组合)与这三个实例不同,它没有一个具体的名称,我'我知道,并且 Cats 并未以任何名称或任何形式提供该信息。
这本身当然不是问题,因为正如我们将在下面看到的,验证此实例是否合法非常容易,但您应该注意一个潜在问题。您并没有真正解释您想要的语义,但如果您想将其提升为 Monoid
,事实上您选择了 Right
当您同时拥有 Left
和一个 Right
意味着你的零必须是 Left
。如果您将右视为成功,将左视为在组合值时可以安全忽略的错误,这可能有点奇怪。
您询问的是Semigroup
但不是Monoid
,所以我们暂时忽略它并证明这个东西是合法的。首先给出定义:
import cats.kernel.Semigroup
implicit def eitherSemigroup[A, B](implicit
A: Semigroup[A],
B: Semigroup[B]
): Semigroup[Either[A, B]] = Semigroup.instance {
case (Right(x), Right(y)) => Right(B.combine(x, y))
case (r @ Right(_), Left(_)) => r
case (Left(_), r @ Right(_)) => r
case (Left(x), Left(y)) => Left(A.combine(x, y))
}
然后是检查部分:
import cats.instances.int._
import cats.instances.string._
import cats.kernel.instances.either.catsStdEqForEither
import cats.kernel.laws.discipline.SemigroupTests
import org.scalacheck.Test.Parameters
SemigroupTests(eitherSemigroup[String, Int]).semigroup.all.check(Parameters.default)
是的,没关系:
+ semigroup.associative: OK, passed 100 tests.
+ semigroup.combineAllOption: OK, passed 100 tests.
+ semigroup.repeat1: OK, passed 100 tests.
+ semigroup.repeat2: OK, passed 100 tests.
就我个人而言,如果我想要这样的东西,我可能会使用包装器来避免混淆我的代码的 future 读者(包括我自己),但考虑到没有人真正知道 Either
的半群是什么。 应该这样做,我认为使用自定义实例不像标准库中的大多数其他类型那样是一个大问题。
关于scala - 半群类型类(任一),组合略有改变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55164691/