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


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

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

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


1918
2018-05-10 18:57


起源

type B 像A一样工作,只是它被命名并且可以被引用,与A.相反。 - pedrofurla


答案:


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

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 输入相同的参数列表。这是斯卡拉目前状态下的主要原因,为什么你需要这个 Aux 模式:目前无法参考 last.B 键入与where相同的参数列表 last 已定义,也不可能有多个隐式参数列表。

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

当然你总是可以写 Last[A] { type B = B0 } 完全没有,但很明显这很快变得非常不切实际(想象一下,使用依赖类型添加几个隐含参数,这对于Shapeless非常常见);那就是 Aux 类型别名进来。


14
2018-05-10 19:20



你不是技术上的 需要 该 Aux 参考的模式 last.B 在相同的参数列表中;它只是首选,因为它比它更简洁 implicit last: Last[A] { type B = B0 }, ord: Ordering[B0]。 - breadmenace
@breadmen嗯是的,从技术上讲,你永远不需要Aux类型的别名。我假设'Aux模式'指的是更宽泛的模式,其中需要类型别名以获得可接受的语法。 - Jasper-M
newb问题 - 为什么不是从计算值推断的隐式defs(使用Aux的那些)的返回类型? - mtk
@ user2359636我很确定如果你让它们实际上是推断它们,但是不要在implicits中添加类型注释是不好的做法。我认为dotty已经取缔了非注释的含义。 - Jasper-M