【问题标题】:How to use path-dependent types with type classes in Scala如何在 Scala 中将路径相关类型与类型类一起使用
【发布时间】:2019-02-17 15:25:28
【问题描述】:

我在使用路径相关类型时遇到了一些问题。

我有一些类型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 编译?有什么我需要补充的证据吗?

【问题讨论】:

    标签: scala types typeclass path-dependent-type


    【解决方案1】:

    看起来有点像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[a.F] 类型的路径相关参数baz 来自外部,编译器插入了一个具体的隐式实例,而实际值@ 987654326@ 不在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

    【讨论】:

      猜你喜欢
      • 2012-10-30
      • 1970-01-01
      • 1970-01-01
      • 2011-02-13
      • 2012-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-27
      相关资源
      最近更新 更多