In scala, how to make type class working for Aux pattern? - Part 2

随声附和 提交于 2021-02-19 03:46:28

问题


This is a follow up question of: In scala, how to make type class working for Aux pattern?

Considering the following example:

  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
  }

all implementations of TypeClasses contains an implicit scope of their underlying TypeClass, among them, the T1 is the most simple and specific definition for ForH, unfortunately it doesn't work. An improvement was proposed by @Dan Simon (in T2), it uses a type parameter to allow the spark compiler to discover ForH <:< Base.Aux[HNil]

Now imagine that I'd like to extend @Dan Simon's solution, such that the type class applies to all classes like ForH for different kinds of HList (a super trait of HNil). 2 natural extensions are in T3 & T4 respectively.

Unfortunately none of them works. The T4 can be explained by the fact that ForH <:< Aux[HList] is invalid, but T3 can't use this excuse. In addition, there is no way to improve it to compile successfully.

Why the type class summoning algorithm failed this case? And what should be done to make the type class pattern actually works?


回答1:


Again, T1.summon(v) doesn't compile because T1.t1 is not a candidate, manually resolved T1.summon(v)(T1.t1) doesn't compile.

For T3 and T4 T3.t1[HNil, Base.ForH], T4.t1[HNil, Base.ForH] would be a candidate

T3.summon(v)(T3.t1[HNil, Base.ForH]) // compiles
T4.summon(v)(T4.t1[HNil, Base.ForH]) // compiles

but the trouble is that H is inferred first and it's inferred to be Nothing but t1[Nothing, Base.ForH] doesn't satisfy type bounds.

So the trouble is not with implicit resolution algorithm, it's ok, the trouble is with type inference (and all we know that it's pretty weak in Scala).

You can prevent H to be inferred too fast as Nothing if you modify 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


来源:https://stackoverflow.com/questions/65853961/in-scala-how-to-make-type-class-working-for-aux-pattern-part-2

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!