无法证明与路径相关类型的等价性

Cannot prove equivalence with a path dependent type

为什么最后的summon编译失败?我该怎么做才能编译它?

import java.time.{LocalDateTime, LocalTime}

trait Circular[T]:
  type Parent

given localTimeCircular: Circular[LocalTime] with
  type Parent = LocalDateTime

final class CircularMap[K, +V]()(using val circular: Circular[K])

val summoned = summon[Circular[LocalTime]]
val daily = new CircularMap[LocalTime, Int]()
println(summoned == daily.circular) // Prints true

summon[localTimeCircular.Parent =:= LocalDateTime]
summon[summoned.Parent =:= LocalDateTime]

summon[daily.circular.Parent =:= LocalDateTime]

Scastie link

它在 Scala 3.0.2 和 3.1.0-RC1 上失败

Scala 3 中没有这个问题。在 Scala 2.13.6 中,类似的代码无法编译

import shapeless.the

import java.time.{LocalDateTime, LocalTime}

trait Circular[T] {
  type Parent
}

implicit object localTimeCircular extends Circular[LocalTime] {
  type Parent = LocalDateTime
}
//  implicit val localTimeCircular: Circular[LocalTime] {type Parent = LocalDateTime} = new Circular[LocalTime] {
//    type Parent = LocalDateTime
//  }

final class CircularMap[K, +V]()(implicit val circular: Circular[K])

val summoned = the[Circular[LocalTime]]
val daily = new CircularMap[LocalTime, Int]()
println(summoned == daily.circular) // Prints true

implicitly[localTimeCircular.Parent =:= LocalDateTime]
implicitly[summoned.Parent =:= LocalDateTime]

//implicitly[daily.circular.Parent =:= LocalDateTime] // doesn't compile

https://scastie.scala-lang.org/DmytroMitin/lsX3fS3ET0ajzChwl2Mctw

(我在一个地方用 the 替换了 implicitly 因为 implicitly 可以破坏类型改进,summon 更像 the)。

让我们简化您的代码,以便弄清楚发生了什么。让我们删除隐式(让我们明确地解决它们)

import java.time.{LocalDateTime, LocalTime}

trait Circular[T] {
  type Parent
}

val localTimeCircular = new Circular[LocalTime] {
  type Parent = LocalDateTime
}

final class CircularMap[K, +V](val circular: Circular[K])

val summoned = localTimeCircular
val daily = new CircularMap[LocalTime, Int](localTimeCircular)
println(summoned == daily.circular) // Prints true

implicitly[localTimeCircular.Parent =:= LocalDateTime]
implicitly[summoned.Parent =:= LocalDateTime]

//implicitly[daily.circular.Parent =:= LocalDateTime] // doesn't compile

问题是路径相关类型的设计使得即使 x == x1 也没有必要 x.T =:= x1.T

确实 summoned == daily.circularsummoned 的类型为 Circular[LocalTime] { type Parent = LocalDateTime }daily.circular 的类型为 Circular[LocalTime]。您是这样指定的:val circular: Circular[K]。因此,您将精化类型 Circular[LocalTime] { type Parent = LocalDateTime }(让我们将其表示为 Circular.Aux[LocalTime, LocalDateTime])升级为其超类型,即没有精化的类型 Circular[LocalTime](也称为存在类型 Circular.Aux[LocalTime, _])。所以类型 localTimeCircular.Parentsummoned.ParentLocalDateTime 但类型 daily.circular.Parent 是抽象的。例如,如果你想恢复类型细化,你可以定义

final class CircularMap[K, +V, P](val circular: Circular[K] {type Parent = P})

另一种方法是使用单例类型

final class CircularMap[K, +V](val circular: localTimeCircular.type)

Is there a way to not lose the refinement Circular[LocalTime] { type Parent = LocalDateTime } without introducing the type parameter P on the class CircularMap? Something like final class CircularMap[K, +V](val circular: Circular[K] { type Parent = X }) where X would introduce a new type variable in the scope of the CircularMap body.

你想要的实际上是 class 上的多个类型参数列表(这样在 class CircularMap[K, +V][P] 中你可以指定 K, V 并推断 P )

https://github.com/scala/bug/issues/4719

https://contributors.scala-lang.org/t/multiple-type-parameter-lists-in-dotty-si-4719/2399

你可以模仿他们

import java.time.{LocalDateTime, LocalTime}

trait Circular[T] {
  type Parent
}

val localTimeCircular = new Circular[LocalTime] {
  type Parent = LocalDateTime
}

trait CircularMap[K, +V] {
  type P
  val circular: Circular[K] {type Parent = P}
}
object CircularMap {
  def apply[K, V]: PartiallyApplied[K, V] = new PartiallyApplied[K, V]

  class PartiallyApplied[K, V] {
    def apply[_P](_circular: Circular[K] {type Parent = _P}): CircularMap[K, V] {type P = _P} = new CircularMap[K, V] {
      override type P = _P
      override val circular: Circular[K] {type Parent = _P} = _circular
    }
  }
}

val summoned = localTimeCircular
val daily = CircularMap[LocalTime, Int](localTimeCircular)
println(summoned == daily.circular) // Prints true

implicitly[localTimeCircular.Parent =:= LocalDateTime]
implicitly[summoned.Parent =:= LocalDateTime]
implicitly[daily.circular.Parent =:= LocalDateTime] // compiles