Ответ 1
Пусть светит на этом другой свет.
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))
}
Итак, что Monoid (s), если мы используем Option[B]
как наш Z
? Scalaз предлагает три. Для первичного экземпляра требуется 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)
Но это не единственный способ объединить два параметра. Вместо добавления двух опций в случае, если они оба Some
, мы могли бы просто выбрать первый или последний из двух. Два триггера это, мы создаем отдельный тип с трюком под названием Tagged Types. Это похоже на Haskell newtype
.
scala> import Tags._
import Tags._
scala> Monoid[Option[Int] @@ First].append(Tag(Some(1)), Tag(Some(2)))
res10: [email protected]@[Option[Int],scalaz.Tags.First] = Some(1)
scala> Monoid[Option[Int] @@ Last].append(Tag(Some(1)), Tag(Some(2)))
res11: [email protected]@[Option[Int],scalaz.Tags.Last] = Some(2)
Option[A] @@ First
, добавленный через него Monoid
, использует в качестве примера те же семантики orElse
.
Итак, все вместе:
scala> Monoid[A => Option[B] @@ First]
res12: scalaz.Monoid[A => [email protected]@[Option[B],scalaz.Tags.First]] =
[email protected]