路徑依賴類型(Path-dependent types) 不知道AbsCell綁定的類型情況下,也可以對其進行訪問。下面這段代碼將一個cell的值恢復成爲其初始值(init),而無需關心cell值的類型是什麼。
def reset(c: AbsCell): unit = c.set(c.init)
爲什麼可以這樣做呢?因爲c.init的類型是c.T,而c.set是c.T=>unit類型的函數,因此形參與實參類型一致,方法調用是類型正確的。
c.T是一個路徑依賴類型的例子,通常來講,這種類型的形式是:x1.x2.….xn.T(n>0),x1,…,xn是不可變的值,而T是xn的類型成員。路徑依賴類型是Scala的一個新穎的特性,其理論基礎是vObj calculus[36]。
路徑依賴類型要依靠其前綴路徑的不可變性,下面給出一個違反了不可變性的例子:
var flip = false
def f(): AbsCell = {
flip = !flip
if (flip) new AbsCell { type T = int; val init = 1 }
else new AbsCell { type T = String; val init = "" }
}
f().set(f().get) // illegal!
在上例中,每一次調用f()分別返回int和String類型的值,因此最後一句是錯誤的,因爲它要將String類型的值賦給一個int值的cell。Scala類型系統禁止這種調用,因爲f().get的類型是f().T,而這不是一個有效類型,因爲f()不是一個有效路徑。
類型選擇與單例類型(Type selection and singleton types)在Java中,類型定義可以嵌套,嵌套類型用其外部類型做前綴的形態表示。在Scala中,則通過“外部類型#內部類型”(Outer#Inner)的方式來表示,“#”就稱作類型選擇(Type Selection)。從概念上說,這與路徑依賴類型(例如:p.Inner)不同,因爲p是一個值,不是一個類型。進一步而言,Outer#t也是一個無效表達式,如果t是一個定義在Outer中的抽象類型的話。
實際上,路徑依賴類型可以被擴展成爲類型選擇,p.t可以看做是p.type#t,這裏p.type就稱作單例類型,僅代表p所指向對象的類型。單例類型本身對於支持方法調用串接很有作用,考慮如下代碼:C有一個incr方法,對其值+1,其子類D由一個decr方法,對其值-1。
class C {
protected var x = 0
def incr: this.type = { x = x + 1; this }
}
class D extends C {
def decr: this.type = { x = x - 1; this }
}
從而我們可以將相關調用串接起來:
val d = new D; d.incr.decr
如果沒有this.type這個單例類型,上述調用是非法的,因爲d.incr的類型應該是C,但C並沒有decr方法。從這個意義上說,this.type類似於Kim Bruce的mytype[29]的一個協變的使用方式。