How to implement this concept of the future / state as monads in scala

I try to implement a container for a match (for example, in sports) so that I can create matches between the winners of other matches. This concept is close to what future monads are, because it contains a certain meaning, and also close to the state monad, because it hides a change in state. Being mostly a beginner on the topic, I implemented the initial version in scala, which will undoubtedly improve. I added a get method, which I'm not sure was a good idea, and so far the only way to create a value would be Unknown(null) , which is not as elegant as I had hoped. What do you think I can do to improve this design?

 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 : an example of using 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)) } }) } 

And the test fragment:

 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 did not have much time to work on this, because my laptop broke down, but I, although it would be useful to write a monad, which I still did 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 and got it, and now instead the TBD class gets a function that will determine the value or null if it is still undefined. This idea is great for the map method, but other methods have subtle errors.

+7
source share
1 answer

For a simple approach, you do not need monads, with a partial application it is enough:

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

This is the starting point, I am sure that it can be finalized. Maybe we will find the elusive monad. But I think that an application functor will be enough. I will give another passage later ...

+2
source

All Articles