Подтвердить что ты не робот

Scala PartialFunction может быть Monoid?

Я думал, что PartialFunction может быть Monoid. Правильно ли мой мыслительный процесс? Например,

import scalaz._
import scala.{PartialFunction => -->}

implicit def partialFunctionSemigroup[A,B]:Semigroup[A-->B] = new Semigroup[A-->B]{
  def append(s1: A-->B, s2: => A-->B): A-->B = s1.orElse(s2)
}

implicit def partialFunctionZero[A,B]:Zero[A-->B] = new Zero[A-->B]{
  val zero = new (A-->B){
    def isDefinedAt(a:A) = false
    def apply(a:A) = sys.error("error")
  }
}

Но текущая версия Scalaz (6.0.4) в нее не включена. Есть ли причина для чего-то не включенного?

4b9b3361

Ответ 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]

Ответ 2

Нет, это выглядит хорошо, удовлетворяя как требования для (некоммутативного) Monoid. Интересная идея. Какой вариант использования вы пытаетесь поддерживать?

Ответ 3

Ваш ноль, безусловно, нарушает аксиому для элемента идентификации, но я думаю, что личная (частичная) функция будет в порядке.

Ваше приложение также не выполняет законы моноида, но вместо orElse вы можете вызвать andThen (состав). Но это будет работать только для A == B:

implicit def partialFunctionSemigroup[A]: Semigroup[A --> A] = new Semigroup[A --> A] {
  def append(s1: A --> A, s2: => A --> A): A-->A = s1 andThen s2
}

implicit def partialFunctionZero[A]: Zero[A --> A] = new Zero[A --> A] {
  val zero = new (A --> A) {
    def isDefinedAt(a:A) = true
    def apply(a:A) = a
  }
}