路径依赖类型没有路径?

时间:2017-06-16 11:31:52

标签: scala dependent-type path-dependent-type

考虑这个简单的例子:

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

2 个答案:

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

您可能还想尝试两个参数列表,因为它有时会影响类型推断。

答案 1 :(得分: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,而不是一些可以由无限量居住的一般类型值。 的一种方式。为此,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())
                                          ^