让我们从不同的角度来看这个问题。
PartialFunction[A, B]
等同于 A => Option[B]
。(实际上,为了能够检查给定的 A
是否定义,而不触发 B
的求值,您需要 A => LazyOption[B]
)
因此,如果我们能找到一个 Monoid[A => Option[B]]
,我们就证明了你的断言。
给定 Monoid[Z]
,我们可以按照以下方式形成 Monoid[A => Z]
:
implicit def readerMonoid[Z: Monoid] = new Monoid[A => Z] {
def zero = (a: A) => Monoid[Z].zero
def append(f1: A => Z, f2: => A => Z) = (a: A) => Monoid[Z].append(f1(a), f2(a))
}
如果我们使用Option[B]
作为我们的Z
,那么我们有哪些Monoid呢?Scalaz 提供了三种。其中主要实例需要一个Semigroup[B]
。
implicit def optionMonoid[B: Semigroup] = new Monoid[Option[B]] {
def zero = None
def append(o1: Option[B], o2: => Option[B]) = o1 match {
case Some(b1) => o2 match {
case Some(b2) => Some(Semigroup[B].append(b1, b2)))
case None => Some(b1)
case None => o2 match {
case Some(b2) => Some(b2)
case None => None
}
}
}
使用这个:
scala> Monoid[Option[Int]].append(Some(1), Some(2))
res9: Option[Int] = Some(3)
但这并不是合并两个 Options 的唯一方法。我们可以选择仅选取两者中的第一个或最后一个,而不是在它们都是 Some
时附加两者的内容。为了实现这个目的,我们使用一种被称为标记类型的技巧创建了一个不同的类型。这在精神上类似于 Haskell 的 newtype
。
scala> import Tags._
import Tags._
scala> Monoid[Option[Int] @@ First].append(Tag(Some(1)), Tag(Some(2)))
res10: scalaz.package.@@[Option[Int],scalaz.Tags.First] = Some(1)
scala> Monoid[Option[Int] @@ Last].append(Tag(Some(1)), Tag(Some(2)))
res11: scalaz.package.@@[Option[Int],scalaz.Tags.Last] = Some(2)
Option[A] @@ First
是通过其Monoid
附加的,使用与您示例相同的orElse
语义。
因此,将所有这些放在一起:
scala> Monoid[A => Option[B] @@ First]
res12: scalaz.Monoid[A => scalaz.package.@@[Option[B],scalaz.Tags.First]] =
scalaz.std.FunctionInstances0$$anon$13@7e71732c
Function1
在组合下是一个幺半群吗? - Daniel C. SobralFunction1[A, A]
,也被称为Endo[A]
,是什么。 - retronym