2011-11-12 10 views
7

Estoy tratando de implementar un contenedor para un resultado de coincidencia (como en el deporte) para poder crear coincidencias entre los ganadores de otras coincidencias. Este concepto está cerca de lo que es una mónada en el futuro, ya que contiene un valor a ser definido, y también cerca de una mónada de estado, ya que oculta el cambio de estado. Siendo principalmente un principiante en el tema, he implementado una versión inicial en scala que seguramente es mejorable. Agregué un método get que no estoy seguro era una buena idea, y hasta ahora la única forma de crear un valor sería Unknown(null) que no es tan elegante como esperaba. ¿Qué crees que podría hacer para mejorar este diseño?cómo implementar este concepto de futuro/estado como una mónada en scala

case class Unknown[T](t : T) { 
    private var value : Option[T] = Option(t) 
    private var applicatives: List[T => Unit] = Nil 

    def set(t: T) { 
    if (known) { 
     value = Option(t) 
     applicatives.foreach(f => f(t)) 
     applicatives = Nil 
    } else { 
     throw new IllegalStateException 
    } 
    } 

    def get : T = value.get 

    def apply(f: T => Unit) = value match { 
    case Some(x) => f(x); 
    case None => applicatives ::= f 
    } 

    def known = value == None 
} 

ACTUALIZACIÓN: un ejemplo de uso de la aplicación de corriente sigue

case class Match(val home: Unknown[Team], val visit: Unknown[Team], val result: Unknown[(Int, Int)]) { 
    val winner: Unknown[Team] = Unknown(null) 
    val loser: Unknown[Team] = Unknown(null) 

    result.apply(result => { 
    if (result._1 > result._2) { 
     home.apply(t => winner.set(t)) 
     visit.apply(t => loser.set(t)) 
    } else { 
     home.apply(t => loser.set(t)) 
     visit.apply(t => winner.set(t)) 
    } 
    }) 
} 

y una prueba de fragmento:

val definedUnplayedMatch = Match(Unknown(Team("A")), Unknown(Team("B")), Unknown(null)); 
val definedPlayedMatch = Match(Unknown(Team("D")), Unknown(Team("E")), Unknown((1,0))); 
val undefinedUnplayedMatch = Match(Unknown(null), Unknown(null), Unknown(null)); 

definedUnplayedMatch.winner.apply(undefinedUnplayedMatch.home.set(_)) 
definedPlayedMatch.winner.apply(undefinedUnplayedMatch.visit.set(_)) 
undefinedUnplayedMatch.result.set((3,1)) 
definedUnplayedMatch.result.set((2,4)) 
undefinedUnplayedMatch.winner.get must be equalTo(Team("B")); 
undefinedUnplayedMatch.loser.get must be equalTo(Team("D")); 

ACTUALIZACIÓN - IDEA ACTUAL: No he tenido Mucho tiempo para trabajar en esto porque mi computadora portátil se averió, pero pensé que sería útil escribir la mónada Tengo hasta el momento para aquellos que estén interesados:

sealed abstract class Determine[+A] { 
    def map[B](f: A => B): Determine[B] 
    def flatMap[B](f: A => Determine[B]): Determine[B] 
    def filter(p: A => Boolean): Determine[A] 
    def foreach(b: A => Unit): Unit 
} 
final case class Known[+A](value: A) extends Determine[A] { 
    def map[B](f: A => B): Determine[B] = Known(f(value)) 
    def flatMap[B](f: A => Determine[B]): Determine[B] = f(value) 
    def filter(p: A => Boolean): Determine[A] = if (p(value)) this else Unknown 
    def foreach(b: A => Unit): Unit = b(value) 
} 
final case class TBD[A](definer:() => A) extends Determine[A] { 
    private var value: A = _ 

    def map[B](f: A => B): Determine[B] = { 
    def newDefiner(): B = { 
     f(cachedDefiner()) 
    } 
    TBD[B](newDefiner) 
    } 

    def flatMap[B](f: A => Determine[B]): Determine[B] = { 
    f(cachedDefiner()) 
    } 

    def filter(p: A => Boolean): Determine[A] = { 
    if (p(cachedDefiner())) 
     this 
    else 
     Unknown 
    } 

    def foreach(b: A => Unit): Unit = { 
    b(cachedDefiner()) 
    } 

    private def cachedDefiner(): A = { 
    if (value == null) 
     value = definer() 
    value 
    } 
} 
case object Unknown extends Determine[Nothing] { 
    def map[B](f: Nothing => B): Determine[B] = this 
    def flatMap[B](f: Nothing => Determine[B]): Determine[B] = this 
    def filter(p: Nothing => Boolean): Determine[Nothing] = this 
    def foreach(b: Nothing => Unit): Unit = {} 
} 

que se deshizo del conjunto & conseguir y ahora la clase TBD recibe en cambio una función que definirá proporcionar el valor o null si aún no definido. Esta idea funciona muy bien para el método de mapa, pero el resto de los métodos tienen errores sutiles.

+0

Sin preocuparse por la implementación, ¿puede dar algunos ejemplos de cómo usaría * tal mónada usando 'flatMap' o' for' comprehensions? En la actualidad, su implementación carece del método 'flatMap' que definiría la semántica en su mónada. – huynhjl

+0

He actualizado la pregunta con el uso de prueba que le estoy dando en este momento. Reconozco que mi implementación es demasiado torpe para aprovecharla para las comprensiones en este momento, básicamente es un puerto de un patrón observador en Java al que le falta el azúcar sintáctico scala. – ilcavero

+0

No es muy claro por sus ejemplos lo que está tratando de lograr al hacer la mónada Desconocida [A]. ¿Puedes contarnos un poco más sobre la aplicación que usará esta biblioteca? – dyross

Respuesta

2

Para un enfoque simple, no es necesario mónadas, la aplicación parcial es suficiente:

//some utilities 
type Score=(Int,Int) 
case class MatchResult[Team](winner:Team,loser:Team) 

//assume no ties 
def playMatch[Team](home:Team,away:Team)(score:Score)= 
    if (score._1>score._2) MatchResult(home,away) 
    else MatchResult(away,home) 

//defined played match 
val dpm= playMatch("D","E")(1,0) 
//defined unplayed match, we'll apply the score later 
val dum= playMatch("A","B")_ 

// a function that takes the dum score and applies it 
// to get a defined played match from an undefined one 
// still is a partial application of match because we don't have the final result yet 
val uumWinner= { score:Score => playMatch (dpm.winner,dum(score).winner) _ } 
val uumLoser= { score:Score => playMatch (dpm.loser,dum(score).loser) _} 

//apply the scores 
uumWinner (2,4)(3,1) 
uumLoser (2,4)(0,1) 


//scala> uumWinner (2,4)(3,1) 
//res6: MatchResult[java.lang.String] = MatchResult(D,B) 
//scala> uumLoser (2,4)(0,1) 
//res7: MatchResult[java.lang.String] = MatchResult(A,E) 

Este es un punto de partida, estoy bastante seguro de que puede ser refinado. Tal vez allí encontremos la esquiva mónada. Pero creo que un functor aplicativo será suficiente. Voy a dar otro pase más tarde ...

+0

esta es una alternativa muy interesante, la principal debilidad que veo es que está imponiendo un orden en la definición de los resultados, y también es más difícil visualizar la progresión del bracket sin los objetos Match. – ilcavero

Cuestiones relacionadas