在 Scala 中,如何使类型类适用于 Aux 模式?- 第2部分 [英] In scala, how to make type class working for Aux pattern? - Part 2

查看:53
本文介绍了在 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 (在 T2 中) 提出了一个改进,它使用类型参数来允许 spark 编译器发现 ForH <:<;Base.Aux[HNil]

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]

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

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.

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

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?

推荐答案

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

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

对于 T3T4 T3.t1[HNil, Base.ForH], T4.t1[HNil, Base.ForH] 将成为候选人

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

但问题是 H 首先被推断,它被推断为 Nothingt1[Nothing, Base.ForH] 没有t 满足类型边界.

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.

所以问题不在于隐式解析算法,没关系,问题在于类型推断(而且我们都知道它在 Scala 中非常弱).

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).

如果你修改T3.t1,T4.t1,你可以防止H过快被推断为Nothing代码>

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

这篇关于在 Scala 中,如何使类型类适用于 Aux 模式?- 第2部分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆