2011-11-12 12 views
7

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

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:現在の実装での使用例

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

とテストスニペットは次の私が持っていなかったを私のラップトップが故障したのでこれに取り組むのに多くの時間が必要ですが、私はモナド私が興味を持っている人のために、これまで持っている:

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がセット&取得し、今TBDクラスではなく、まだ未定義の場合、値はnullを提供定義する機能を受信を処分しました。この考え方はmapメソッドにとっては効果的ですが、残りのメソッドは微妙なバグがあります。

+0

「flatMap」または「for」内包語を使用してモナドを使用する方法についていくつかの例を挙げることはできますか?あなたの実装は現在、あなたのモナドのセマンティックを定義する 'flatMap'メソッドがありません。 – huynhjl

+0

私は今使っているテストの使い方で質問を更新しました。現時点では、私の実装は理解が難しいと認識していますが、基本的にはJavaのオブザーバパターンのポートであり、スカラ構文的な砂糖が足りません。 – ilcavero

+0

Unknown [A]モナドを作る際に達成しようとしていることは、あなたの例からあまり明確ではありません。このライブラリを使用するアプリケーションについてもう少し詳しく教えてください。 – dyross

答えて

2

単純なアプローチのために、あなたは部分的に適用して、モナドを必要としませんが十分である:

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

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

+0

これは非常に興味深い選択肢です。私が見る主な弱点は、結果の定義に秩序を課していることですMatchオブジェクトなしでブラケットの進行を視覚化することは難しいです。 – ilcavero

関連する問題