[英]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推断
X
为Outer
,这不够具体,我们无论如何都可以提供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
成为单例类型,这样它只能表示路径o1
或o2
,而不是一些可以由无限量值居住的一般类型。 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.