2012-10-02 64 views
3

我試圖在斯卡拉模擬代數半環。我遇到了類型轉換問題,這使得看起來不可能在Scala中做我想做的事情。我希望有人能指引我理解我誤解的Scala輸入系統的方面。如何在Scala中使用運行時工廠對象來實現半實現?

(請長設置爲這個問題承受的。我已經削減下來,就像我可以。)

半環超過其定義二進制加法(+)和乘法一組項目(*)運算符及其標識元素,分別稱爲零和一。例如,Integer Semiring定義在整數上,其中+和*是來自算術和零的標準操作,一個是整數0和1.一個更奇特的例子是布爾半環,它是根據值True和False定義的,其中+是邏輯或,*是邏輯與,零是假,一個是真。

爲了建模,我定義了一個特徵來指定適當的二元運算符。

trait SemiringElement { 
    /** 
    * The element type 
    */ 
    type E 
    /** 
    * The type returned by the the addition and multiplication operators 
    */ 
    type R <: SemiringElement 
    val value: E 

    def +(that: R): R 

    def *(that: R): R 

    override def toString = value.toString 
} 

案例類實例化特定半環的元素。例如,布爾半環看起來像這樣。

case class BooleanSemiringElement(init: Boolean) extends SemiringElement { 
    type E = Boolean 
    type R = BooleanSemiringElement 
    val value = init 

    def +(that: BooleanSemiringElement#R) = BooleanSemiringElement(value || that.value) 

    def *(that: BooleanSemiringElement#R) = BooleanSemiringElement(value && that.value) 
} 

我還有一個指定零和一個元素的Semiring特徵。

trait Semiring { 
    type E <: SemiringElement 
    /** 
    * The addition identity 
    */ 
    val zero: E 
    /** 
    * The multiplication identity 
    */ 
    val one: E 
} 

特定的半環對象返回零和一個適當類型的元素。

object BooleanSemiring extends Semiring { 
    type E = BooleanSemiringElement 

    val zero = BooleanSemiringElement(false) 
    val one = BooleanSemiringElement(true) 
} 

的半環的對象基本上是工廠單身知道如何返回適當類型的識別元素。

我希望能夠編寫一般用於半環元素的算法。我使用Semiring工廠對象,以便能夠在運行時指定特定的半環,而不是編譯時間。例如,假設我有一個對象在字符串和半環元素之間保持一個映射。

class ElementMap(s: Semiring) { 
    val m = mutable.Map[String, SemiringElement]() 
} 

如果我實例化這個,象這樣一個電話:

val x = new ElementMap(BooleanSemiring) 

我想x.m是一個與字符串> BooleanSemiringElement地圖。問題是我所擁有的實際上是一個String-> SemiringElement映射。

scala> val x = new ElementMap(BooleanSemiring) 
x: ElementMap = [email protected] 
scala> x.m 
res2: scala.collection.mutable.Map[String,SemiringElement] = Map() 
scala> x.m("one") = BooleanSemiring.one 
scala> x.m("one") + BooleanSemiring.one 
<console>:12: error: type mismatch; 
found : BooleanSemiring.one.type (with underlying type BooleanSemiring.BooleanSemiringElement) 
required: _1.R where val _1: SemiringElement 
       x.m("one") + BooleanSemiring.one 
            ^

如果我願意在編譯時指定類型,而不是運行時我可以做的元素類型的通用像這樣:

class ElementMap[BooleanSemiring]... 

但後來我需要一個工廠方法來創建所有不同類型的ElementMap對象。將工廠智慧融入Semiring特質使其更具建築意義。我想可以說是這樣的:

class ElementMap(s: Semiring) { 
    val m = mutable.Map[String, s.E]() 
} 

是:創建一個從字符串由供給構造的半環對象返回的元素類型E的地圖。我無法弄清楚如何做到這一點。我嘗試了各種語法技巧和隱式轉換無濟於事。

有沒有辦法寫一個在運行時配置了Semiring構造函數參數的ElementMap,還是我採用了錯誤的方法?我是新來的Scala,並試圖以斯卡拉式的方式做事。我覺得自己已經在這裏闖入了一個角落,但我不確定這個失誤究竟在哪裏。

回答

0

您是否嘗試過捕獲具體類型s作爲類型參數?

scala> class ElementMap[S <: Semiring](s: S) { 
    | val m = collection.mutable.Map[String, S#E]() 
    | } 
defined class ElementMap 

scala> val x = new ElementMap(BooleanSemiring) 
x: ElementMap[BooleanSemiring.type] = [email protected] 

scala> x.m("one") = BooleanSemiring.one 

scala> x.m("one") + BooleanSemiring.one 
res0: BooleanSemiringElement = true 

你就可以用元素類型類似的東西:

scala> class ElementMap[SE <: SemiringElement](s: Semiring { type E = SE }) { 
    | val m = collection.mutable.Map[String, SE]() 
    | } 
defined class ElementMap 

scala> val x = new ElementMap(BooleanSemiring) 
x: ElementMap[BooleanSemiringElement] = [email protected] 

scala> x.m("one") = BooleanSemiring.one 

scala> x.m("one") + BooleanSemiring.one 
res1: BooleanSemiringElement = true 

與您的版本的問題是,你把自己的所有類型的信息有關s - 從的ElementMap的角度來看,它只是一個Semiring

(作爲一個側面說明,通過類型類特設多態可能是解決斯卡拉這個問題更自然的方式。例如,見Scalaz 7表示類似的代數結構的方式。)

+0

感謝。即席多態似乎確實是針對這種情況。我現在正在閱讀。斯卡拉茲有一個具體的例子來解決這個問題嗎? –

+0

'Monoid'可能是最接近的。 –