简体   繁体   English

路径依赖类型没有路径?

[英]Path-dependent types without a path?

Consider this trivial example: 考虑这个简单的例子:

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

As expected, this doesn't compile because of a type mismatch: 正如预期的那样,由于类型不匹配,因此无法编译:

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

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

What if we want to define a standalone function? 如果我们想要定义一个独立的功能怎么办?

This is no good 这不好

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

because this compiles as if everything is OK 因为这会编译好像一切都好

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

This works 这有效

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

because this compiles: 因为这编译:

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

and this doesn't: 这不是:

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

However we have to pass an extra Outer argument to test . 但是我们必须传递一个额外的Outer参数来test Is it possible to avoid this? 有可能避免这种情况吗? Ideally, the following should work: 理想情况下,以下应该有效:

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

This seems to work: 这似乎有效:

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())
           ^

You might also want to experiment with two parameter lists as it sometimes affects type inference. 您可能还想尝试两个参数列表,因为它有时会影响类型推断。

I don't think that, out of the box, it's possible to enforce it in a satisfying way. 我不认为,开箱即用,可以以令人满意的方式执行它。 For instance, one possible solution might be: 例如,一种可能的解决方案可能是:

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())
                                ^

Looks good, but you can circumvent it by explicitly passing in the type arguments. 看起来不错,但您可以通过显式传入类型参数来绕过它。 (same goes for @OlivierBlanvillain's solution by the way) (顺便说一句@ OlivierBlanvillain的解决方案)

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

Now let's try the following: 现在让我们尝试以下方法:

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())

Doesn't work, scalac infers X to be Outer , which isn't specific enough, and we could supply Outer as explicit type argument anyway. 不起作用,scalac推断XOuter ,这不够具体,我们无论如何都可以提供Outer作为显式类型参数。 We need a way to force X to be a singleton type so that it can only represent the path o1 or o2 , and not some general type that can be inhabited by an infinite amount of values. 我们需要一种方法来强制X成为单例类型,这样它只能表示路径o1o2 ,而不是一些可以由无限量值居住的一般类型。 There is a way. 一个办法。 Scala has the marker trait Singleton for this purpose. 为此,Scala具有标记特征Singleton Let's try it: 我们来试试吧:

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())
                    ^

Now our valid case doesn't work anymore! 现在我们的有效案例不再适用了! The problem is that scalac refuses to infer singleton types. 问题是scalac拒绝推断单例类型。 We have to pass them in explicitly: 我们必须明确地传递它们:

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

The invalid cases don't work anymore: 无效案例不再有效:

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())
           ^

So this enforces the rules we want, but you have to pass in the types explicitly... 所以这强制执行我们想要的规则,但你必须明确地传递类型......


EDIT 编辑

Actually it turns out you can enforce this without losing type inference and without help from any external libraries, but you're probably not going to like it :-p 事实上,事实证明你可以强制执行此操作而不会丢失类型推断,也没有任何外部库的帮助,但你可能不会喜欢它:-p

META EDIT as pointed out in the comments this can still be circumvented if you try hard enough, so I guess you're stuck with the above solution. 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())
                                          ^

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM