2017-06-16 67 views
4

考慮這個簡單的例子:沒有路徑的路徑相關類型?

class Outer { 
    case class Inner() 
    def test(i: Inner) = {} 
} 

正如預期的那樣,這並不因爲類型不匹配的編譯:

val o1 = new Outer() 
val o2 = new Outer() 

o1.test(o2.Inner()) // doesn't compile 

如果我們想定義一個獨立的功能?

這是沒有好

def test[X <: Outer](a: X#Inner, b: X#Inner) = {} 

,因爲編譯如下,如果一切正常

test(o1.Inner(), o2.Inner()) 

這工作

def test(x: Outer)(a: x.Inner, b: x.Inner) = {} 

因爲這編譯:

test(o1)(o1.Inner(), o1.Inner()) 

,這並不:

test(o1)(o1.Inner(), o2.Inner()) 

但是我們不得不額外Outer參數傳遞給test。可以避免這種情況嗎?理想情況下,下面應該工作:

test(o1.Inner(), o1.Inner()) // ok 
test(o1.Inner(), o2.Inner()) // compilation error 

回答

4

我不認爲,開箱即可,以令人滿意的方式強制執行。例如,一種可能的解決方案可能是:

scala> def test[X <: Outer#Inner](a: X)(b: X) =() 
test: [X <: Outer#Inner](a: X)(b: X)Unit 

scala> test(o1.Inner())(o1.Inner()) 

scala> test(o1.Inner())(o2.Inner()) 
<console>:16: error: type mismatch; 
found : o2.Inner 
required: o1.Inner 
     test(o1.Inner())(o2.Inner()) 
           ^

看起來不錯,但您可以通過顯式傳入類型參數來繞過它。 (同樣的方式去爲@ OlivierBlanvillain的解決方案)

scala> test[Outer#Inner](o1.Inner())(o2.Inner()) 

現在,讓我們嘗試以下操作:

scala> def test[X <: Outer](a: X#Inner)(b: X#Inner) =() 
test: [X <: Outer](a: X#Inner)(b: X#Inner)Unit 

scala> test(o1.Inner())(o2.Inner()) 

不行的,scalac推斷XOuter,這是不夠具體,無論如何,我們可以提供Outer作爲顯式類型參數。我們需要一種強制X爲單身類型的方法,以便它只能表示路徑o1o2,而不是某種可以由無限多的值居住的一般類型。有的一種方法。斯卡拉爲此具有標記特徵Singleton。我們來試試吧:

scala> def test[X <: Outer with Singleton](a: X#Inner)(b: X#Inner) =() 
test: [X <: Outer with Singleton](a: X#Inner)(b: X#Inner)Unit 

scala> test(o1.Inner())(o1.Inner()) 
<console>:15: error: inferred type arguments [Outer] do not conform to method test's type parameter bounds [X <: Outer with Singleton] 
     test(o1.Inner())(o1.Inner()) 
    ^
<console>:15: error: type mismatch; 
found : o1.Inner 
required: X#Inner 
     test(o1.Inner())(o1.Inner()) 
        ^

現在我們的有效案例不再適用了!問題是scalac拒絕推斷單身人士類型。我們必須通過他們明確:

scala> test[o1.type](o1.Inner())(o1.Inner()) 

無效的情況下不工作了:

scala> test(o1.Inner())(o2.Inner()) 
<console>:16: error: inferred type arguments [Outer] do not conform to method test's type parameter bounds [X <: Outer with Singleton] 
     test(o1.Inner())(o2.Inner()) 
    ^
<console>:16: error: type mismatch; 
found : o1.Inner 
required: X#Inner 
     test(o1.Inner())(o2.Inner()) 
        ^

scala> test[o1.type](o1.Inner())(o2.Inner()) 
<console>:16: error: type mismatch; 
found : o2.Inner 
required: o1.Inner 
     test[o1.type](o1.Inner())(o2.Inner()) 
             ^

scala> test[Outer](o1.Inner())(o2.Inner()) 
<console>:17: error: type arguments [Outer] do not conform to method test's type parameter bounds [X <: Outer with Singleton] 
     test[Outer](o1.Inner())(o2.Inner()) 
     ^

所以這個強制執行我們想要的規則,但你必須在類型傳遞明確.. 。


編輯

其實原來你可以執行此操作時不會丟失類型推斷,也不需要任何外部庫的幫助,但您可能不會喜歡它:-p

META EDIT如評論中指出的那樣,如果您嘗試足夠努力,仍然可以繞過,所以我想你是堅持以上解決方案。

scala> import scala.language.existentials 
import scala.language.existentials 

scala> def test[X <: x.Inner forSome { val x: Outer }](a: X, b: X) =() 
test: [X <: x.Inner forSome { val x: Outer }](a: X, b: X)Unit 

scala> test(o1.Inner(), o1.Inner()) 

scala> test(o1.Inner(), o2.Inner()) 
<console>:16: error: inferred type arguments [Outer#Inner] do not conform to method test's type parameter bounds [X <: x.Inner forSome { val x: Outer }] 
     test(o1.Inner(), o2.Inner()) 
    ^
<console>:16: error: type mismatch; 
found : o1.Inner 
required: X 
     test(o1.Inner(), o2.Inner()) 
        ^
<console>:16: error: type mismatch; 
found : o2.Inner 
required: X 
     test(o1.Inner(), o2.Inner()) 
           ^

scala> test[o1.Inner](o1.Inner(), o2.Inner()) 
<console>:16: error: type mismatch; 
found : o2.Inner 
required: o1.Inner 
     test[o1.Inner](o1.Inner(), o2.Inner()) 
             ^
+0

不錯的工作! :-) – OlivierBlanvillain

+0

好乞丐不能選擇器:)你仍然可以通過編寫例如'test [x.Inner forSome {val x:Outer}](o1.Inner(),o2.Inner())''但它總比沒有好。 –

+0

當然是哦。<我想我應該想到... –

4

這似乎工作:

scala> def t[X <: Outer#Inner, Y <: Outer#Inner](a: X, b: Y)(implicit e: X =:= Y)=1 
test: [X <: Outer#Inner, Y <: Outer#Inner](a: X, b: Y)(implicit ev: X =:= Y)Unit 

scala> test(o1.Inner(), o1.Inner()) // ok 

scala> test(o1.Inner(), o2.Inner()) 
<console>:15: error: Cannot prove that o1.Inner =:= o2.Inner. 
     test(o1.Inner(), o2.Inner()) 
     ^

你可能也想有兩個參數列表進行試驗,因爲它有時會出現的類型推斷。