如何解决在 Scala 中,如何使类型类适用于 Aux 模式? - 第2部分
这是一个后续问题:In scala,how to make type class working for Aux pattern?
考虑以下示例:
trait Base {
type Out
def v: Out
}
object Base {
type Aux[T] = Base { type Out = T }
type Lt[T] = Base { type Out <: T }
class ForH() extends Base {
final type Out = HNil
override def v: Out = HNil
}
object ForH extends ForH
}
trait TypeClasses {
class TypeClass[B]
def summon[B](b: B)(implicit ev: TypeClass[B]): TypeClass[B] = ev
}
object T1 extends TypeClasses {
implicit def t1: TypeClass[Base.Aux[HNil]] = new TypeClass[Base.Aux[HNil]]
implicit def t2: TypeClass[Int] = new TypeClass[Int]
}
object T2 extends TypeClasses {
implicit def t1[T <: Base.Aux[HNil]]: TypeClass[T] = new TypeClass[T]
}
object T3 extends TypeClasses {
implicit def t1[
H <: HList,T <: Base.Lt[H]
]: TypeClass[T] = new TypeClass[T] {
type HH = H
}
}
object T4 extends TypeClasses {
implicit def t1[
H <: HList,T <: Base.Aux[H]
]: TypeClass[T] = new TypeClass[T] {
type HH = H
}
}
it("No Aux") {
val v = 2
T1.summon(v) // works
}
it("Aux1") {
val v = new Base.ForH()
T1.summon(v) // oops
T1.summon(Base.ForH) // oops
val v2 = new Base.ForH(): Base.Aux[HNil]
T1.summon(v2) // works!
}
it("Aux2") {
val v = new Base.ForH()
T2.summon(v) // works
T2.summon(Base.ForH) // works
val v2 = new Base.ForH(): Base.Aux[HNil]
T2.summon(v2) // works
}
it("Aux3") {
val v = new Base.ForH()
T3.summon(v) // oops
T3.summon(Base.ForH) // oops
val v2 = new Base.ForH(): Base.Aux[HNil]
T3.summon(v2) // oops
}
it("Aux4") {
val v = new Base.ForH()
T4.summon(v) // oops
T4.summon(Base.ForH) // oops
val v2 = new Base.ForH(): Base.Aux[HNil]
T4.summon(v2) // oops
}
TypeClasses
的所有实现都包含其底层 TypeClass
的隐式作用域,其中,T1
是 ForH
最简单、最具体的定义,不幸的是它没有不行。 @Dan Simon 提出了一个改进(在 T2
中),它使用类型参数来允许 Spark 编译器发现 ForH <:< Base.Aux[HNil]
现在想象一下,我想扩展@Dan Simon 的解决方案,这样类型类适用于所有类,如 ForH
等不同类型的 HList(HNil 的一个超级特征)。 2 个自然扩展分别位于 T3
和 T4
。
不幸的是,它们都不起作用。 T4
可以解释为 ForH <:< Aux[HList]
无效,但 T3
不能使用这个借口。另外也没有办法改进编译成功。
为什么类型类召唤算法在这种情况下失败了?应该怎么做才能使类型类模式真正起作用?
解决方法
同样,T1.summon(v)
无法编译,因为 T1.t1
不是候选对象,手动解析的 T1.summon(v)(T1.t1)
无法编译。
对于 T3
和 T4
T3.t1[HNil,Base.ForH]
,T4.t1[HNil,Base.ForH]
将成为候选
T3.summon(v)(T3.t1[HNil,Base.ForH]) // compiles
T4.summon(v)(T4.t1[HNil,Base.ForH]) // compiles
但问题是 H
首先被推断,它被推断为 Nothing
但 t1[Nothing,Base.ForH]
不满足类型边界。
所以问题不在于隐式解析算法,没关系,问题在于类型推断(而且我们都知道它在 Scala 中非常弱)。
如果您修改 H
,Nothing
T3.t1
被推断为 T4.t1
过快
object T3 extends TypeClasses {
implicit def t1[
H <: HList,T <: Base/*.Lt[H]*/
](implicit ev: T <:< Base.Lt[H]): TypeClass[T] = new TypeClass[T] {
type HH = H
}
}
object T4 extends TypeClasses {
implicit def t1[
H <: HList,T <: Base/*.Aux[H]*/
](implicit ev: T <:< Base.Aux[H]): TypeClass[T] = new TypeClass[T] {
type HH = H
}
}
T3.summon(v) // compiles
T4.summon(v) // compiles
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。