路径相关类型(在类型和内部类上)

时间:2019-01-11 17:00:00

标签: scala types path-dependent-type

尝试路径依赖类型时,我遇到了一些意外的结果:

object Funny1 {
  class X {
    type Y = String
    val y: Y = "y"
  }

  val x1 = new X
  val x2 = new X

  def foo(x: X)(y: x.Y): Unit = ()
  def foo_diff(y1: x1.Y)(y2: x2.Y): Unit = () // 1.3
  def foo_gen(y1: X#Y)(y2: X#Y) : Unit = ()

  foo(x1)(x1.y)
  foo(x1)(x2.y) // <-- 1.1 would expect this to fail

  foo_diff(x1.y)(x2.y)
  foo_diff(x2.y)(x2.y) // <-- 1.2 would expect this to fail

  foo_gen(x1.y)(x2.y)
  foo_gen(x2.y)(x2.y)
}


object Funny2 {
  class X {
    class Y {
    }
  }

  val x1 = new X
  val x2 = new X

  val x1y = new x1.Y
  val x2y = new x2.Y

  def foo(x: X)(y: x.Y): Unit = ()
  def foo_diff(y1: x1.Y)(y2: x2.Y): Unit = ()
  def foo_gen(y1: X#Y)(y2: X#Y) : Unit = ()

  foo(x1)(x1y)
  // foo(x1)(x2y) // does not compile

  foo_diff(x1y)(x2y)
  // foo_diff(x2y)(x2y) // does not compile

  foo_gen(x1y)(x2y)
  foo_gen(x2y)(x2y)
}

object Funny3 {
  trait X {
    type Y
    def y: Y
  }

  val x1 = new X {
    override type Y = String

    override def y: String = "y"
  }
  val x2 = new X {
    override type Y = Int

    override def y: Int = 3
  }

  def foo(x: X)(y: x.Y): Unit = ()
  def foo_diff(y1: x1.Y)(y2: x2.Y): Unit = ()
  def foo_gen(y1: X#Y)(y2: X#Y) : Unit = ()

  foo(x1)(x1.y)
  // foo(x1)(x2.y)    // 3.1 fails as expected

  foo_diff(x1.y)(x2.y)
  // foo_diff(x2.y)(x2.y)  // 3.2 fails as expected

  foo_gen(x1.y)(x2.y)
  foo_gen(x2.y)(x2.y)
}



object Funny3b {
  trait X {
    type Y
    def y: Y
  }

  val x1 = new X {
    override type Y = String

    override def y: String = "y"
  }
  val x2 = new X {
    override type Y = String

    override def y: String = "y2"
  }

  def foo(x: X)(y: x.Y): Unit = ()
  def foo_diff(y1: x1.Y)(y2: x2.Y): Unit = ()
  def foo_gen(y1: X#Y)(y2: X#Y) : Unit = ()

  foo(x1)(x1.y)
  foo(x1)(x2.y)    // 3b.1 does not fail

  foo_diff(x1.y)(x2.y)
  foo_diff(x2.y)(x2.y)  // 3b.2 does not fail

  foo_gen(x1.y)(x2.y)
  foo_gen(x2.y)(x2.y)
}

特别是,我对问题的答案感兴趣:

  • 为什么标有1.1和1.2)的行可以编译?
  • 为什么没有引用x作为1.3 foo_diff中所需的第一个参数?
  • 为什么第3.1和3.2行不能编译,但是3b.1和3b.2可以编译?尤其是在3b中,路径依赖似乎“丢失”了,如果可以将基础类型解析为相同的类型(此处为String)。

谢谢马丁

1 个答案:

答案 0 :(得分:1)

我认为,所有问题背后的基本误解是您认为一种构造:

trait / class ClassName {
  type T = something
} 

创建一个依赖类型。没有。如果您打开规范第3.5节“类型之间的关系”的3.5.1 Equivalence小节,您可能会看到:

  
      
  • 如果t是由类型别名类型t = T定义的,则t等效于T
  •   

这就是为什么在您的第一个示例中,编译器将x1.Yx2.YX#Y视为String。如果仅用String代替所有代码,那么为什么要编译该代码就没有问题了。

类似地,您的第三个示例仅定义了一些别名。如果用这些别名替换它们的定义,这将再次清楚为什么代码会编译并失败。

您的第二个示例使用了不同的构造

trait / class ClassName {
  trait / class InnerName
} 

这是一种创建依赖于路径的类型的构造,因此您希望不编译的代码实际上失败了。据我了解,这是您唯一真正涉及依赖类型的示例。

此外,如果您打开介绍第一个和第二个构造的Scala之旅Abstract Type MembersInner Classes,您可能会注意到,只有后者(而不是前者!)提到“路径-依赖类型”。