繁体   English   中英

理解 Scala 类型系统中的 Aux 模式

[英]Understanding the Aux pattern in Scala Type System

之前可能会问和回答这个问题,但我想通过一个例子来理解这一点,我无法推理出 Aux 模式可能有帮助的地方! 所以这是特征:

trait Foo[A] {
  type B
  def value: B
}

为什么我有一个类型绑定到值函数的返回类型? 我这样做能达到什么目的? 特别是,我会在哪里使用这样的模式?

想象一个用于获取任何元组的最后一个元素的类型类。

trait Last[A] {
  type B
  def last(a: A): B
}

object Last {
  type Aux[A,B0] = Last[A] { type B = B0 }

  implicit def tuple1Last[A]: Aux[Tuple1[A],A] = new Last[Tuple1[A]] {
    type B = A
    def last(a: Tuple1[A]) = a._1
  }

  implicit def tuple2Last[A,C]: Aux[(A,C),C] = new Last[(A,C)] {
    type B = C
    def last(a: (A,C)) = a._2
  }

  ...
}

类型B始终取决于类型A ,这就是为什么A是类型类的输入类型而B是输出类型的原因。

现在,如果您想要一个可以根据最后一个元素对任何元组列表进行排序的函数,您需要访问同一参数列表中的B类型。 这就是在 Scala 的当前状态下为什么需要Aux模式的主要原因:目前不可能在定义last的同一参数列表中引用last.B类型,也不可能有多个隐式参数列表。

def sort[A,B](as: List[A])(implicit last: Last.Aux[A,B], ord: Ordering[B]) = as.sortBy(last.last)

当然,你总是可以完整地写出Last[A] { type B = B0 } ,但显然这很快就会变得非常不切实际(想象一下,添加几个具有依赖类型的隐式参数,这在 Shapeless 中很常见); 这就是Aux类型别名的用武之地。

同一个参数列表中支持启动 Scala 3 依赖类型,这似乎使Aux模式变得不必要,例如,Jasper-M 的代码片段简化为

trait Last[A]:
  type B
  def last(a: A): B

given [A]: Last[Tuple1[A]] with
  type B = A
  def last(a: Tuple1[A]) = a._1

given [A, C]: Last[(A,C)] with
  type B = C
  def last(a: (A,C)) = a._2

def sort[A](as: List[A])(using last: Last[A], ord: Ordering[last.B]) = as.sortBy(last.last)

sort(List(("ffle",3), ("fu",2), ("ker",1)))
// List((ker,1), (fu,2), (ffle,3))

注意last.B的用法,其中last是来自同一个参数列表中的值

def sort[A](as: List[A])(using last: Last[A], ord: Ordering[last.B])

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM