Scala PartialFunction может быть Monoid? - PullRequest
14 голосов
/ 30 января 2012

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

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) в нее не входит.Есть ли причина чего-то не входит?

Ответы [ 3 ]

28 голосов
/ 31 января 2012

Давайте посветим по-другому:

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
2 голосов
/ 30 января 2012

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

0 голосов
/ 30 января 2012

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

Ваше приложение также не соответствует законам Моноида, но вместо Или Else Вы можете позвонить и затем (состав).Но это будет работать только для 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
  }
}
...