2017-06-16 66 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()) 
     ^

你可能也想有两个参数列表进行试验,因为它有时会出现的类型推断。