2010-04-09 51 views
6

我想使用對象實例爲模塊/函子,更多或更少的,如下所示:如何在Scala中使用對象作爲模塊/函子?

abstract class Lattice[E] extends Set[E] { 
    val minimum: E 
    val maximum: E 
    def meet(x: E, y: E): E 
    def join(x: E, y: E): E 
    def neg(x: E): E 
} 

class Calculus[E](val lat: Lattice[E]) { 
    abstract class Expr 
    case class Var(name: String) extends Expr {...} 
    case class Val(value: E) extends Expr {...} 
    case class Neg(e1: Expr) extends Expr {...} 
    case class Cnj(e1: Expr, e2: Expr) extends Expr {...} 
    case class Dsj(e1: Expr, e2: Expr) extends Expr {...} 
} 

所以,我可以爲每個晶格創建不同的演算實例(我將執行的操作需要其的信息是格子的最大值和最小值)。我希望能夠混合相同演算的表達式,但不能混合不同表達式。到現在爲止還挺好。我可以創建我的微積分實例,但問題是我不能在其他操作它們的類中編寫函數。

例如,我想創建一個解析器從文件中讀取表達式並返回它們;我還試圖編寫一個隨機表達式生成器用於我的ScalaCheck測試。原來每次函數生成一個Expr對象時,我都不能在函數之外使用它。即使我創建了Calculus實例並將其作爲參數傳遞給函數,該函數又會生成Expr對象,但函數的返回結果不會被識別爲與函數外部創建的對象類型相同。

也許我的英語不夠清楚,讓我試試,我想做些什麼玩具的例子(不是真正的ScalaCheck發電機,但足夠接近)。現在

def genRndExpr[E](c: Calculus[E], level: Int): Calculus[E]#Expr = { 
    if (level > MAX_LEVEL) { 
    val select = util.Random.nextInt(2) 
    select match { 
     case 0 => genRndVar(c) 
     case 1 => genRndVal(c) 
    } 
    } 
    else { 
    val select = util.Random.nextInt(3) 
    select match { 
     case 0 => new c.Neg(genRndExpr(c, level+1)) 
     case 1 => new c.Dsj(genRndExpr(c, level+1), genRndExpr(c, level+1)) 
     case 2 => new c.Cnj(genRndExpr(c, level+1), genRndExpr(c, level+1)) 
    } 
    } 
} 

,如果我嘗試編譯上面的代碼中,我得到很多的

 
error: type mismatch; 
found : plg.mvfml.Calculus[E]#Expr 
required: c.Expr 
     case 0 => new c.Neg(genRndExpr(c, level+1)) 

而同樣的情況,如果我嘗試做這樣的事情:

val boolCalc = new Calculus(Bool) 
val e1: boolCalc.Expr = genRndExpr(boolCalc) 

請注意,生成器本身並不關心,但我需要在系統的其他部分做很多類似的事情(即創建和操作微積分實例表達式)。

我做錯了什麼? 是否有可能做我想做的事情?

在這個問題上的幫助是非常需要和讚賞。提前致謝。


從Apocalisp收到答案並嘗試它之後。

非常感謝您的回答,但仍然存在一些問題。所提出的解決方案是函數的簽名改爲:

def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr 

我改變了簽名所涉及的所有功能:getRndExpr,getRndVal和getRndVar。而我得到了相同的錯誤消息,我到處都調用這些函數,並得到了以下錯誤消息:

 
error: inferred type arguments [Nothing,C] do not conform to method genRndVar's 
type parameter bounds [E,C <: plg.mvfml.Calculus[E]] 
     case 0 => genRndVar(c) 

由於編譯器似乎無法找出正確的類型我改變了所有的函數調用是象下面這樣:

case 0 => new c.Neg(genRndExpr[E,C](c, level+1)) 

在此之後,在第一2次函數調用(genRndVal和genRndVar)沒有編譯錯誤,但在以下3次調用(遞歸調用genRndExpr),其中該函數的返回用於建立一新的Expr對象我得到以下錯誤:

 
error: type mismatch; 
found : C#Expr 
required: c.Expr 
     case 0 => new c.Neg(genRndExpr[E,C](c, level+1)) 

所以,我再次卡住了。任何幫助將不勝感激。

+0

您的問題標題有點誤導。考慮類似於「如何將內部類從外部相對於類範圍引用?」 – Alexey 2010-04-10 14:28:52

回答

3

問題是斯卡拉不能統一這兩種類型Calculus[E]#ExprCalculus[E]#Expr

雖然那些看起來一樣,對不對?那麼,考慮你可以在某種類型的E上有兩種不同的結石,每種都有自己的Expr類型。而你不想混合兩者的表情。

您需要以這樣一種方式限制類型,即返回類型與Calculus參數的內部類型Expr類型相同。你要做的是什麼:

def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr 
+0

嗨!非常感謝答案,但仍然存在一些問題。我試過你的解決方案。我改變了所有涉及函數的簽名:getRndExpr,getRndVal和getRndVar。在我稱之爲這些功能的地方,我收到了同樣的錯誤信息。 由於缺乏空間,我無法完全描述問題,所以我將編輯問題的主體以便能夠正確回覆。 – Jeff 2010-04-10 03:13:46

1

如果你不想獲得從微積分具體的演算,然後只要將表達式來全局範圍或通過全球範圍是指它:

class Calculus[E] { 
    abstract class Expression 
    final type Expr = Calculus[E]#Expression 

    ... the rest like in your code 
} 

this question指的是完全相同的問題。

如果你想使微積分的一個亞型,並重新定義Expr的有(什麼是不可能的),你必須:

把getRndExpr到微積分類或放getRndExpr成衍生性狀:

trait CalculusExtensions[E] extends Calculus[E] { 
    def getRndExpr(level: Int) = ... 
    ... 
} 

請參考this線程爲什麼如此。

相關問題