简体   繁体   English

如何在Scala中使用具有类型类的路径依赖类型

[英]How to use path-dependent types with type classes in Scala

I'm having some problems with path dependent types. 我在路径依赖类型方面遇到了一些问题。

I have some types Foo with an abstract type member F . 我有一些类型Foo与抽象类型成员F Instances such as Bar will provide the concrete type. Bar等实例将提供具体类型。

Then there is a type class Baz . 然后有一个类型Baz I have instances of the type class for each concrete type of Foo#F (but not for Foo itself). 我为每个具体类型的Foo#F都有类型类的实例(但不适用于Foo本身)。

Here is an example: 这是一个例子:

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)
}

I can't get this to compile: 我无法编译:

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]

This does compile: 这确实编译:

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)

Why does g compile but not f ? 为什么g编译而不是f Aren't the types the same? 这些类型不一样吗?

How can I get f to compile? 如何编译f Is there some sort of evidence I need to add? 我需要添加某种证据吗?

Seems somewhat similar to this question . 似乎有点类似于这个问题 Here is a way to make it compile: 这是一种使其编译的方法:

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)

Your g compiles, because the path-dependent argument baz of type Baz[aF] comes from the outside , a concrete implicit instance is inserted by the compiler, and the actual value a isn't used anywhere inside g . 你的g编译,因为Baz[aF]类型的路径相关参数baz来自外部 ,编译器插入一个具体的隐式实例,而实际值a不在g内的任何地方使用。

Your f does not compile, because the B[aF] appears only in the return type, and it cannot be made more concrete before an actual argument a is passed to f . 你的f不能编译,因为B[aF] 出现在返回类型中,并且在将实际参数a传递给f之前不能使它更具体。

In a sense, the f breaks the path between the argument a and the returned value, because it makes the following "discontinuous jumps": 从某种意义上说, f打破了参数a和返回值之间的路径,因为它会产生以下“不连续跳转”:

  • Start with a: Foo a: Foo开始
  • Jump from a to the Bar singleton (by pattern-matching) a跳转到Bar singleton(通过模式匹配)
  • Use g to get from the concrete Bar singleton to the concrete Baz[Array[Byte]] 使用g从具体的Bar单例到混凝土Baz[Array[Byte]]
  • try to return this Baz[Array[Byte]] , which seems no longer connected to Baz[aF] . 尝试返回此Baz[Array[Byte]] ,它似乎不再连接到Baz[aF]

This path can be repaired by proving that the discontinuous "jump" is indeed just an identity path that stays in the same spot all the time, so it really doesn't move anywhere, so that aF and the inferred type are the same, namely T . 这条路径可以通过证明不连续的“跳跃”确实只是一直保持在同一点的标识路径来修复,所以它实际上不会移动到任何地方,因此aF和推断类型是相同的,即T

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM