使用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
上的猫?
答案 0 :(得分:10)
Either
有一堆合法的半群实例,而其中的哪个some debate应该包含在Cats中。在这方面,Cats,Scalaz和Haskell都做出了不同的选择,而您描述的实例(翻转但左右合并)与所有这三个实例都不相同,因此我没有特定的名称知道,并且Cats并未以任何名称或任何形式提供它。
这本身并不是问题,因为正如我们将在下面看到的那样,很容易验证此实例是否合法,但是您应该意识到一个潜在的问题。您并没有真正解释您想要的语义,但是如果您想将其提升为Monoid
,那么当您同时拥有Right
和{ {1}}意味着您的零必须为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)
的半群应该是什么< / em>,我认为使用自定义实例不会像标准库中的大多数其他类型那样麻烦。