在 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 的隐式作用域,其中,T1ForH 最简单、最具体的定义,不幸的是它没有不行。 @Dan Simon 提出了一个改进(在 T2 中),它使用类型参数来允许 Spark 编译器发现 ForH <:< Base.Aux[HNil]

现在想象一下,我想扩展@Dan Simon 的解决方案,这样类型类适用于所有类,如 ForH 等不同类型的 HList(HNil 的一个超级特征)。 2 个自然扩展分别位于 T3T4

不幸的是,它们都不起作用。 T4 可以解释为 ForH <:< Aux[HList] 无效,但 T3 不能使用这个借口。另外也没有办法改进编译成功。

为什么类型类召唤算法在这种情况下失败了?应该怎么做才能使类型类模式真正起作用?

解决方法

同样,T1.summon(v) 无法编译,因为 T1.t1 不是候选对象,手动解析的 T1.summon(v)(T1.t1) 无法编译。

对于 T3T4 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 首先被推断,它被推断为 Nothingt1[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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...