この未来/州の概念をスカラのモナドとして実装する方法

私は他のマッチの勝者の間でマッチを作ることができるように(スポーツのような)マッチのコンテナを実装しようとしています。このコンセプトは、未来のモナドは、それが定義された値を含み、状態変化を隠す状態モナドの近くにあるので、近いものです。主にトピックの初心者であるため、私はscalaで初期バージョンを実装しましたが、それは確かに改善可能です。
getメソッドを追加しましたが、これは良いアイデアではありませんでした。値を作成する唯一の方法は、
Unknown(null)です。このデザインを改善するために私は何ができると思いますか?

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

そしてテストスニペット:

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.

ベストアンサー

簡単なアプローチでは、部分的なアプリケーションで十分ですが、モナドは必要ありません。

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

これは出発点ですが、私はそれがさらに洗練されていると確信しています。たぶんそこにはわかりにくいモナドがあります。しかし、私はアプリケーションのファンクタで十分だろうと思う。
私は後で別のパスを与える…

コメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です