没有路径的路径相关类型?
Path-dependent types without a path?
考虑这个简单的例子:
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
这似乎有效:
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())
^
您可能还想尝试使用两个参数列表,因为它有时会影响类型推断。
我认为,开箱即用,不可能以令人满意的方式强制实施。例如,一种可能的解决方案可能是:
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 将 X
推断为 Outer
,这不够具体,我们可以提供 Outer
作为显式类型参数。我们需要一种方法来强制 X
成为单例类型,以便它只能表示路径 o1
或 o2
,而不是某种可以容纳无限数量的通用类型值。 是一个方法。为此,Scala 具有标记特征 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())
^
考虑这个简单的例子:
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
这似乎有效:
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())
^
您可能还想尝试使用两个参数列表,因为它有时会影响类型推断。
我认为,开箱即用,不可能以令人满意的方式强制实施。例如,一种可能的解决方案可能是:
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 将 X
推断为 Outer
,这不够具体,我们可以提供 Outer
作为显式类型参数。我们需要一种方法来强制 X
成为单例类型,以便它只能表示路径 o1
或 o2
,而不是某种可以容纳无限数量的通用类型值。 是一个方法。为此,Scala 具有标记特征 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())
^