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

spa*_*rkr 12 scala shapeless

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

trait Foo[A] {
  type B
  def value: B
}
Run Code Online (Sandbox Code Playgroud)

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

Jas*_*r-M 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
  }

  ...
}
Run Code Online (Sandbox Code Playgroud)

类型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)
Run Code Online (Sandbox Code Playgroud)

当然你总是可以Last[A] { type B = B0 }完全写出来,但显然这很快变得非常不切实际(想象一下,在依赖类型中增加一些隐含的参数,这与Shapeless非常相似); 这就是Aux类型别名的来源.

  • 从技术上讲,您不需要“Aux”模式来引用同一参数列表中的“last.B”;它只是首选,因为它比 `implicit last: Last[A] { type B = B0 }, ord: Ordering[B0]` 简洁得多。 (2认同)
  • newb问题 - 为什么不是从计算值推断的隐式defs(使用Aux的那些)的返回类型? (2认同)
  • @ user2359636我很确定如果你让它们实际上是推断它们,但是不要在implicits中添加类型注释是不好的做法.我认为dotty已经取缔了非注释的含义. (2认同)

Mar*_*lic 12

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

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))
Run Code Online (Sandbox Code Playgroud)

注意last.Bwhere lastis a value的用法来自同一个参数列表

def sort[A](as: List[A])(using last: Last[A], ord: Ordering[last.B])
Run Code Online (Sandbox Code Playgroud)