2011-11-12 26 views
7

我想實現一個匹配容器(如運動)的結果,以便我可以在其他匹配的獲勝者之間創建匹配。這個概念接近於未來的monad,因爲它包含一個被定義的價值,並且接近一個狀態monad,因爲它隱藏了狀態變化。作爲主題上的大多數人,我已經在scala中實現了初始版本,這肯定是可以改進的。我添加了一個get方法,我不知道這是一個好主意,到目前爲止,創建一個值的唯一方法是Unknown(null),這不像我所希望的那樣優雅。你認爲我可以做些什麼來改進這種設計?如何在scala中實現這個未來/狀態概念作爲monad

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:目前執行的使用示例如下

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

更新 - 當前的IDEA:我沒有因爲我的筆記本電腦壞了,所以我有很多時間來處理這個問題,但我認爲編寫monad會很有用我至今對於那些有興趣誰:

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

我擺脫了一套&得到現在的TBD類接收,而不是將定義如果仍不明確提供值或空值的函數。這個想法對於map方法非常有用,但其餘的方法都有微妙的錯誤。

+0

不用擔心實現,你可以舉一些例子說明你將如何使用這樣一個使用'flatMap'或'for'解析的monad?你的實現目前缺乏定義monad上的語義的'flatMap'方法。 – huynhjl

+0

我已經用我現在給它的測試用法更新了這個問題。我意識到我的實現過於笨拙,無法利用當前的理解,基本上它是Java中觀察者模式的一個端口,缺少了scala語法糖。 – ilcavero

+0

從你的例子中不清楚你在製作未知[A] monad時想要達到的效果。你能告訴我們更多關於將使用這個庫的應用程序嗎? – 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) 

這是一個起點,我敢肯定它可以進一步完善。也許我們會找到難以捉摸的monad。但我認爲應用函子就足夠了。 我稍後會再給你一遍...

+0

這是一個非常有趣的選擇,我看到的主要弱點是它在結果的定義中強加一個順序,很難在沒有匹配對象的情況下顯示支架進展。 – ilcavero