2017-06-14 92 views
2

我對Scala中的類型投影有限制。假設我有以下代碼:斯卡拉類型投影的正確限制是什麼?

sealed trait Color 
case object Red extends Color 
case object Green extends Color 
case object Blue extends Color 

trait Item { 
    type Colors <: Color 
} 

object RedGreenItem extends Item { 
    type Colors = Red.type with Green.type 
} 

object Test { 
    def foo[I <: Item, C >: I#Colors <: Color](item : I, color : C) : Unit = { 
    // ... 
    } 

    def main(args : Array[String]) : Unit = { 
    foo(RedGreenItem, Blue) // <- why does it compile? 
    } 
} 

什麼其實我是想實現的是能夠傳遞給foo作爲第二個參數只在Item#Colors定義的顏色(因此限制C >: I#Colors)。 但事實證明,我可以通過其他所有顏色(在這種情況下爲Blue)。

,我可以看到這個問題的根源,如果我寫的foo方法是這樣的:

def foo[I <: Item, C >: I#Colors <: Color : TypeTag](item : I, color : C) : Unit = { 
    // ... 
    println(s"type = ${typeOf[C]}") 
} 

// type = Color 

所以,與其Blue.type類型被推斷爲Color,因此任何顏色是好的。 如果我刪除限制>: I#Colors,那麼該類型是正確的決定:

def foo[I <: Item, C <: Color : TypeTag](item : I, color : C) : Unit = { 
    // ... 
    println(s"type = ${typeOf[C]}") 
} 

// type = Blue.type 

所以,我的問題是:我如何能實現我的目標是有限制的顏色有一個允許的類型,而不是編譯除此以外?非常感謝,並提前抱歉,也許是一個愚蠢的問題。

回答

3

你的類型Colors基本上是說你想要你的顏色都是RedGreen。但是你要表達的是它必須是RedGreen

在斑點狗,未來斯卡拉的版本,你可以表達比較直接,你想:

sealed trait Color 
sealed trait Red extends Color 
sealed trait Green extends Color 
sealed trait Blue extends Color 
case object Red extends Red 
case object Green extends Green 
case object Blue extends Blue 

trait Item { 
    type Colors <: Color 
} 

object RedGreenItem extends Item { 
    type Colors = Red | Green 
} 

def foo[I <: Item](item: I)(color: item.Colors): Unit =() 

額外的密封特性是規避可能仍然被舉起的其他限制。但這個工程:

scala> foo(RedGreenItem)(Red) 
scala> foo(RedGreenItem)(Green) 
scala> foo(RedGreenItem)(Blue) 
-- [E007] Type Mismatch Error: <console>:13:18 --------------------------------- 
13 |foo(RedGreenItem)(Blue) 
    |     ^^^^ 
    |     found: Blue.type 
    |     required: RedGreenItem.Colors 
    |  

在當前斯卡拉我認爲最接近這種類型成員的模型,在一般情況下,是一個無形HList

import shapeless._, ops.hlist._ 

trait Item { 
    type Colors <: HList 
} 

object RedGreenItem extends Item { 
    type Colors = Red.type :: Green.type :: HNil 
} 

def foo[I <: Item, C <: Color](item: I, color: C)(implicit contains: Selector[item.Colors, C]): Unit =() 

,這再次按預期工作:

scala> foo(RedGreenItem, Red) 

scala> foo(RedGreenItem, Green) 

scala> foo(RedGreenItem, Blue) 
<console>:27: error: Implicit not found: shapeless.Ops.Selector[RedGreenItem.Colors, Blue.type]. You requested an element of type Blue.type, but there is none in the HList RedGreenItem.Colors. 
     foo(RedGreenItem, Blue) 
     ^

還有別的方式來表達這一點。例如用類型類而不是可重寫的類型成員。我不推薦使用HLists,但如果你想要使用類型成員,就可以這樣做。

+0

謝謝賈斯珀!這看起來像我在找什麼。我對HList有一些想法,所以我打算這樣做。 – Max

3

對於這樣的事情來說,無形狀的拉動聽起來像是一個巨大的矯枉過正。 也許這樣的事情?

trait Item { 
    trait Acceptable[C <: Color] 
} 

object RedGreenItem extends Item { 
    implicit object R extends Acceptable[Red.type] 
    implicit object G extends Acceptable[Green.type] 
} 

def foo[I <: Item, C <: Color : I#Acceptable](i: I, c: C) = println(c) 

foo(RedGreenItem, Red) 
// Red 

foo(RedGreenItem, Blue) 
error: could not find implicit value for evidence parameter of type RedGreenItem.Acceptable[Blue.type] 
+0

我喜歡'Acceptable'是'Item'的內在特徵。但是如果你想有'Item'的子類而不是不再工作的對象。 –

+0

這也是一個有趣的解決方案,謝謝。 – Max