无法证明与路径相关类型的等价性
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]
它在 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.circular
但 summoned
的类型为 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.Parent
和 summoned.Parent
是 LocalDateTime
但类型 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
为什么最后的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]
它在 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.circular
但 summoned
的类型为 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.Parent
和 summoned.Parent
是 LocalDateTime
但类型 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 parameterP
on the classCircularMap
? Something likefinal class CircularMap[K, +V](val circular: Circular[K] { type Parent = X })
whereX
would introduce a new type variable in the scope of theCircularMap
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