首页 > 解决方案 > 在 scala 中,如何使类型类适用于 Aux 模式?- 第2部分

问题描述

这是一个后续问题:在 scala 中,如何使类型类适用于 Aux 模式?

考虑以下示例:

  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 (in ) 提出了一项改进T2,它使用类型参数来允许 spark 编译器发现ForH <:< Base.Aux[HNil]

现在想象一下,我想扩展@Dan Simon 的解决方案,以便类型类适用于所有类,例如ForH不同种类的 HList(HNil 的超级特征)。2 个自然扩展分别在T3&T4中。

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

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

标签: scalatypeclassabstract-data-typetype-aliasscala-2.13

解决方案


同样,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.t1T4.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

推荐阅读