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

私は他のマッチの勝者の間でマッチを作ることができるように(スポーツのような)マッチのコンテナを実装しようとしています。このコンセプトは、未来のモナドは、それが定義された値を含み、状態変化を隠す状態モナドの近くにあるので、近いものです。主にトピックの初心者であるため、私は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.

7
実装について心配することなく、 flatMapfor を使用してモナドを使用する例をいくつか挙げることができますか?あなたの実装には現在、あなたのモナドのセマンティックを定義する flatMap メソッドがありません。
追加された 著者 huynhjl,
@ilcavero、 artima.com/pins1ed/forをご覧ください。 -expressions-revisited.html#23.6flatMap を使用してコードを再構築する方法をご覧ください。
追加された 著者 huynhjl,
あなたのテストは副作用に依存しているので、必須のコードです。戻り値を使用せずにメソッドを呼び出すだけです。
追加された 著者 ziggystar,
モナドの「重要な」部分は、M [X]を持ち、関数X => M [X]を適用したいメソッドbind(flatMap)です。あなたはそれをどのように使用する予定ですか?あなたは本当にモナドが必要ないかもしれない
追加された 著者 GClaramunt,
@ziggystar同意、あなたは何も特別な必要はありません
追加された 著者 GClaramunt,
私は今使っているテストの使い方で質問を更新しました。現時点では、私の実装は理解が難しいと認識していますが、基本的にはJavaのオブザーバパターンのポートであり、スカラ構文的な砂糖が足りません。
追加された 著者 ilcavero,
@ DavidY.Rossという考えは、マッチの括弧を作ることができることです。マッチには必ずしも競争相手や結果が定義されているわけではありません。ゲームがまだプレイされていなければ、知られているかもしれない。モナドコンテナは、競合者および/または結果が不明であるという事実からマッチを抽象化することを可能にする。
追加された 著者 ilcavero,
@huynhjlもし私ができるなら、それは私が立ち往生した場所だと思うし、おそらく私はJavaであまりにも多くを考えているからだろう。私は、Unknownのサブクラスを使用し、地図の戻り値にクロージャを使用するアイデアがあると信じていますが、私は座って試してみる必要があります。
追加された 著者 ilcavero,
@GClaramunt私は本当にそれを私自身で理解することができないので、私は質問した。私はそれを使う予定がある以外に、一度(未定義から定義された)状態をモナドの良い候補に変えるTo Be Determinedの概念ではないのですか?
追加された 著者 ilcavero,
@ziggystarは同意しましたが、 'definedUnplayedMatch.winner.apply(undefinedUnplayedMatch.ho‌ me.set(_))'のような行が悪化することに気づきました。 'val undefinedUnplayedMatch = Match(definedUnplayedMatch.winner、definedPlayedMatch .winner、Unknown(null)) 'は同等であり、それほど重要ではありません。マッチはいつも同じ勝者を返します。最初は不明なモナドの中では未定義です
追加された 著者 ilcavero,
Unknown [A]モナドを作る際に達成しようとしていることは、あなたの例からはあまり明確ではありません。このライブラリを使用するアプリケーションについてもう少し詳しく教えてください。
追加された 著者 dyross,
@ilcavero、おそらくあなたはコンテナ/モナドとしてブラケットを考える必要があります。マッチアップを取って勝者を返す関数を使って、 "マッチアップ"のブラケット/ツリー上に "マップ"します。
追加された 著者 dyross,

1 答え

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

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

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

2
追加された
これは非常に興味深い選択肢です。私が目にする弱点は、結果の定義に秩序を課していることです。また、Matchオブジェクトを使用しないでブラケットの進行を視覚化することも難しいです。
追加された 著者 ilcavero,