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

时间:2017-05-10 18:57:10

标签: scala shapeless

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

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

为什么我的类型绑定到值函数的返回类型?我这样做了什么?特别是,我会在哪里使用这种模式?

2 个答案:

答案 0 :(得分:14)

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

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.B所在的参数列表中引用last类型是不可能的。已定义,也不可能有多个隐式参数列表。

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类型别名的来源。

答案 1 :(得分:7)

Scala 3在相同参数列表中支持dependent types,这似乎不需要Aux模式,for example,Jasper-M的代码段简化为

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

object Last {    
  given [A] as Last[Tuple1[A]] {
    type B = A
    def last(a: Tuple1[A]) = a._1
  }

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

def sort[A](as: List[A])(implicit 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])(implicit last: Last[A], ord: Ordering[last.B])