2012-01-30 17 views
13

Pensé PartialFunction puede ser Monoid. ¿Mi proceso de pensamiento es correcto? Por ejemplo,Scala PartialFunction puede ser 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") 
    } 
} 

Pero Scalaz versión actual (6.0.4) que no está incluido. ¿Hay alguna razón para algo no incluido?

+0

Asumo que son conscientes de que 'Function1' es un monoide bajo composición? –

+1

@dcsobral 'Function1 [A, A]', aka 'Endo [A]', es. – retronym

Respuesta

28

Brillemos una luz diferente sobre esto.

PartialFunction[A, B] es isomorfo a A => Option[B]. (En realidad, para poder comprobar si se ha definido para un determinado A sin desencadenar la evaluación de la B, se necesitaría A => LazyOption[B])

Así que si podemos encontrar un Monoid[A => Option[B]] hemos probado su afirmación.

Dado Monoid[Z], podemos formar Monoid[A => Z] de la siguiente manera:

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

Por lo tanto, lo que Monoid (s) tenemos si usamos como nuestro Option[B]Z? Scalaz proporciona tres. La instancia principal requiere un 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 
    } 
    } 
} 

El uso de este:

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

Pero eso no es la única manera de combinar dos opciones. En lugar de anexar el contenido de las dos opciones en el caso de que ambas sean Some, podríamos simplemente elegir la primera o la última de las dos. Dos activan esto, creamos un tipo distinto con truco llamado Tipos Etiquetados. Esto es similar en espíritu a Haskell's 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, anexa a través de su Monoid, utiliza los mismos orElse semántica que tu ejemplo.

lo tanto, poner todo esto junto:

scala> Monoid[A => Option[B] @@ First] 
res12: scalaz.Monoid[A => [email protected]@[Option[B],scalaz.Tags.First]] = 
     [email protected] 
+0

¡Muchas gracias! No me había dado cuenta de que PartialFunction es isomorfo a A => LazyOption [B] –

+0

Gracias, @retronym! Los tipos etiquetados están disponibles solo en scalaz-seven, para la versión anterior es necesario usar el rasgo de FirstOption, ¿estoy en lo cierto? – lester

+2

@lester yep, exactamente. Los tipos etiquetados tienen algunos bordes afilados, desafortunadamente, es posible que necesitemos un mejor soporte scalac antes de recomendarlos. El ejemplo es: 'List (Tag (1))' da una 'ClassCastException' ya que una parte del compilador trata los argumentos como una matriz de objetos, y una parte posterior como una matriz primitiva. – retronym

2

No, esto se ve bien, satisfaciendo los dos requisitos para Monoid (no conmutativo). Idea interesante. ¿Qué caso de uso estás tratando de apoyar?

+0

Lo sentimos, pero la identidad se viola claramente. –

+1

@Heiko Lo siento, pero su declaración es claramente incorrecta. Incluso si la respuesta es incorrecta, está lejos de ser clara (al menos para mí). – ziggystar

0

Su cero ciertamente viola el axioma del elemento de identidad, pero creo que la función de identidad (parcial) estaría bien.

Su append no cumplir con las leyes monoid, pero en lugar de OrElse se podría llamar andthen (composición). Pero esto solo funcionaría para 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 
    } 
} 
+0

¿Puedes dar un contraejemplo? – ziggystar

+0

Ley de identidad: ea = a = ae –

+0

¿Para qué se violan 'e' y' a'? – ziggystar

Cuestiones relacionadas