hoe dit toekomst/staat-concept als een monade in scala te implementeren

Ik probeer een container te implementeren voor een match (zoals in sport), zodat ik wedstrijden kan maken tussen de winnaars van andere wedstrijden. Dit concept komt dicht in de buurt van wat een toekomstige monade is, omdat het een te definiëren waarde bevat en ook dicht bij een staatsmadad omdat het staatsverandering verbergt. Omdat ik meestal een beginner ben over het onderwerp, heb ik een eerste versie in scala geïmplementeerd die zeker kan worden verbeterd. Ik heb een methode get get toegevoegd waarvan ik niet zeker weet of het een goed idee is, en tot nu toe is de enige manier om een ​​waarde te maken Unknown (null) , wat niet zo elegant is als ik had gehoopt. Wat denk je dat ik zou kunnen doen om dit ontwerp te verbeteren?

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
}

UPDATE: a usage example of the current implementation follows

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

En een testfragment:

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

UPDATE - CURRENT IDEA : I haven't had much time to work on this because my laptop broke down, but I though it would be useful to write the monad I have so far for those who are interested:

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

I got rid of the set & get and now the TBD class receives instead a function that will define provide the value or null if still undefined. This idea works great for the map method, but the rest of the methods have subtle bugs.

7
Maakt u zich geen zorgen over de implementatie, kunt u enkele voorbeelden geven van hoe u een dergelijke monade zou gebruiken met behulp van flatMap of voor -betekenis? Uw implementatie ontbreekt momenteel de flatMap -methode die de semantiek op uw monade zou definiëren.
toegevoegd de auteur huynhjl, de bron
@ilcavero, kunt u artima.com/pins1ed/for bekijken? -expressies-revisited.html # 23.6 en hoe u uw code kunt herstructureren met behulp van flatMap ?
toegevoegd de auteur huynhjl, de bron
Uw test is afgekapt als verplichte code omdat u vertrouwt op bijwerkingen. Je belt alleen methoden zonder hun retourwaarden te gebruiken.
toegevoegd de auteur ziggystar, de bron
Het "belangrijke" deel van de monade is de methode bind (flatMap) waar u een M [X] hebt en een functie X => M [X] wilt toepassen. Hoe ben je van plan om dat te gebruiken? Misschien heb je geen monade echt nodig
toegevoegd de auteur GClaramunt, de bron
@ziggystar eens, met veranderlijkheid heb je niets speciaals nodig
toegevoegd de auteur GClaramunt, de bron
Ik heb de vraag bijgewerkt met het testgebruik dat ik nu aan het geven ben. Ik erken dat mijn implementatie te onhandig is om op dit moment te profiteren van de begrippen, eigenlijk is het een poort van een waarnemerspatroon in Java dat scala syntactische suiker mist.
toegevoegd de auteur ilcavero, de bron
@ DavidY.Ross het idee is om een ​​haakje met wedstrijden te kunnen maken, waarbij een wedstrijd niet noodzakelijk de kanshebbers of het resultaat heeft omdat de kanshebbers nog onbekend zijn als ze afhankelijk zijn van het resultaat van de vorige match en het resultaat kan bekend zijn als het spel nog niet is gespeeld. De monad-container maakt het mogelijk om de wedstrijden te abstraheren uit het feit dat de kanshebbers en/of het resultaat onbekend zijn.
toegevoegd de auteur ilcavero, de bron
@huynhjl Ik zou het doen als ik kon !, ik vermoed dat ik daar vast zit en dat komt waarschijnlijk omdat ik te veel denk in Java. Ik geloof dat ik een idee heb met een subklasse van Onbekend en gebruik maak van sluitingen voor de geretourneerde waarde van de kaart, maar ik zou moeten gaan zitten om het te proberen.
toegevoegd de auteur ilcavero, de bron
@GClaramunt Ik kan het echt niet zelf uitvogelen daarom heb ik de vraag gesteld. Afgezien van wat ik van plan was om het te gebruiken, is niet het concept van een Te Beslissen waarde dat de staat eenmaal verandert (van ongedefinieerd naar gedefinieerd) een goede kandidaat voor een monade?
toegevoegd de auteur ilcavero, de bron
@ziggystar ging akkoord, maar het viel me op dat dit verergerd wordt door regels als 'definedUnplayedMatch.winner.apply (undefinedUnplayedMatch.ho & zwnj; me.set (_))' die onnodig zijn omdat 'val undefinedUnplayedMatch = Match (definedUnplayedMatch.winner, defined PlayMatch .winner, Unknown (null)) is gelijkwaardig en minder noodzakelijk. Een match retourneert altijd dezelfde winnaar, alleen dat deze aanvankelijk niet wordt gedefinieerd in de onbekende monade
toegevoegd de auteur ilcavero, de bron
Het is niet erg duidelijk uit je voorbeelden wat je probeert te bereiken bij het maken van de onbekende [A] monade. Kun je ons wat meer vertellen over de applicatie die deze bibliotheek gaat gebruiken?
toegevoegd de auteur dyross, de bron
@ilcavero, misschien wil je de beugel zien als de container/monade. Je "kaart" over de haakjes/boom van "matchups" met een functie die een matchup neemt en een winnaar teruggeeft.
toegevoegd de auteur dyross, de bron

1 antwoord

Voor een eenvoudige aanpak heb je geen monaden nodig, met gedeeltelijke toepassing is genoeg:

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

Dit is een startpunt, ik ben er vrij zeker van dat het verder kan worden verfijnd. Misschien vinden we daar de ongrijpbare monade. Maar ik denk dat een applicatieve functor genoeg zal zijn. Ik geef later nog een pass

2
toegevoegd
dit is een zeer interessant alternatief, de belangrijkste zwakte die ik zie is dat het een volgorde oplegt in de definitie van de resultaten, en het is ook moeilijker om de haakjesverloop zonder Match-objecten te visualiseren.
toegevoegd de auteur ilcavero, de bron