如何在Scala中将依赖于路径的类型与类型类一起使用

时间:2018-09-12 22:47:37

标签: scala types typeclass path-dependent-type

我在依赖路径的类型方面遇到一些问题。

我有一些类型为Foo的抽象类型成员为FBar之类的实例将提供具体类型。

然后有一个类型类Baz。对于每种Foo#F的具体类型,我都有类型类的实例(但对于Foo本身没有)。

这里是一个例子:

sealed trait Foo {
  type F
}

object Bar extends Foo {
  type F = Array[Byte]
}

trait Baz[B] {
  def b(b: B): String
}

object Baz {
  implicit val bazByteArray: Baz[Array[Byte]] = (b: Array[Byte]) => new String(b)
}

我无法编译它:

def f(a: Foo): Baz[a.F] = {
  val baz = a match {
    case bar@Bar => g(bar)
  }
  baz
} // Expression of type Baz[(a.type with Bar.type)#F] doesn't conform to Baz[a.F]

val x2: Foo = Bar
val y2: Baz[x2.F] = f(x2) // Expression of type Baz[Foo#F] doesn't conform to expected type Baz[x2.F]

这确实可以编译:

def g(a: Foo)(implicit baz: Baz[a.F]): Baz[a.F] = {
  baz
}

val x1: Bar.type = Bar
val y1: Baz[x1.F] = f(x1)

为什么g会编译,而f不会编译?类型不一样吗?

如何获取f进行编译?我需要补充一些证据吗?

1 个答案:

答案 0 :(得分:4)

似乎有点to this question。这是一种使其编译的方法:

sealed trait Foo {
  type F
  def asSingleton: FooSingleton[F]
}

trait FooSingleton[X] extends Foo {
  type F = X
  def asSingleton: FooSingleton[X] = this
}

object Bar extends FooSingleton[Array[Byte]]

trait Baz[B] {
  def b(b: B): String
}

object Baz {
  implicit val bazByteArray: Baz[Array[Byte]] = 
    (b: Array[Byte]) => new String(b)
}

def g(a: Foo)(implicit baz: Baz[a.F]): Baz[a.F] = {
  baz
}

val x1: Bar.type = Bar
val y1: Baz[x1.F] = f(x1)

def f[T](a: Foo { type F = T } ): Baz[T] = {
  (a.asSingleton: FooSingleton[T]) match {
    case bar @ Bar => g(bar)
  }
}

val x2: Foo = Bar
val y2: Baz[x2.F] = f(x2)

您的g会进行编译,因为类型为baz的依赖于路径的参数Baz[a.F]来自外部 ,因此编译器会插入一个具体的隐式实例,并且a内部的任何地方都没有使用实际值g

您的f无法编译,因为B[a.F]在返回类型中仅显示为 ,并且在实际参数a之前无法更具体地进行说明。传递给f

从某种意义上说,f打破了参数a与返回值之间的路径,因为它产生了以下“不连续跳转”:

  • a: Foo开头
  • a跳到Bar单身(通过模式匹配)
  • 使用g从具体的Bar单例到具体的Baz[Array[Byte]]
  • 尝试返回此Baz[Array[Byte]],似乎不再与Baz[a.F]连接。

可以通过证明不连续的“跳跃”确实只是一条始终保持同一位置的身份路径来修复此路径,因此它实际上不会在任何地方移动,因此a.F和推断的类型相同,即T