Frage Scala PartialFunction kann Monoid sein?


ich dachte Teilfunktion kann sein Monoid. Ist mein Denkprozess korrekt? Beispielsweise,

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")
  }
}

Aber aktuelle Version Scalaz (6.0.4) ist nicht enthalten. Gibt es einen Grund dafür, dass etwas nicht enthalten ist?


13
2018-01-30 17:18


Ursprung


Antworten:


Lassen Sie uns ein anderes Licht darauf werfen.

PartialFunction[A, B] ist isomorph zu A => Option[B]. (Eigentlich, um prüfen zu können, ob es für eine gegebene definiert ist A ohne Auslösung der Auswertung B, du bräuchtest A => LazyOption[B])

Also wenn wir eine finden können Monoid[A => Option[B]] Wir haben deine Behauptung bewiesen.

Gegeben Monoid[Z]wir können formen Monoid[A => Z] wie folgt:

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))
}

Also, was Monoid (s) haben wir, wenn wir es benutzen Option[B] als unser Z? Scalaz bietet drei. Die primäre Instanz benötigt a 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
    }
  }
}

Verwenden Sie dies:

scala> Monoid[Option[Int]].append(Some(1), Some(2))
res9: Option[Int] = Some(3)

Aber das ist nicht die einzige Möglichkeit, zwei Optionen zu kombinieren. Anstatt den Inhalt der beiden Optionen anzuhängen, sind sie beide Somekönnten wir einfach den ersten oder den letzten der beiden auswählen. Zwei triggern das, wir erstellen einen eindeutigen Typ mit einem Trick namens Tagged Types. Dies ist dem Geist von Haskell ähnlich 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, angehängt durch es ist Monoid, benutzt das selbe orElse Semantik als Beispiel.

Also, alles zusammen setzen:

scala> Monoid[A => Option[B] @@ First]
res12: scalaz.Monoid[A => scalaz.package.@@[Option[B],scalaz.Tags.First]] = 
       scalaz.std.FunctionInstances0$$anon$13@7e71732c

28
2018-01-30 23:03



Nein, das sieht gut aus und erfüllt beide Anforderungen für (nichtkommutative) Monoid. Interessante Idee. Welchen Anwendungsfall versuchen Sie zu unterstützen?


2
2018-01-30 18:52



Ihre Null verletzt sicherlich das Axiom für das Identitätselement, aber ich denke, dass die (Teil-) Identitätsfunktion in Ordnung wäre.

Ihr Anhang erfüllt auch nicht die Monoid-Gesetze, sondern statt oderElse du könntest anrufen und dann (Zusammensetzung). Aber das würde nur für A == B funktionieren:

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
  }
}

0
2018-01-30 19:08