Давайте посветим по-другому:
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
?Скалаз предоставляет три.Основной экземпляр требует 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.По духу это похоже на 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