Aux 模式介绍
Aux
模式不是一种模式而是一种在每个库中使用的技术,它正在进行某种类型级别的编程,我们需要使用它来克服一个Scala限制。
每次我们在Scala中进行类型级计算时,我们都会在另一个类/特质中定义一个类型别名,让我们看一个例子:
trait Foo[A] {
type B
def value: B
}
例如,在这种情况下,类型级计算的结果将存储在B
中。
所以让我们定义一些实例:
implicit def fi = new Foo[Int] {
type B = String
val value = "Foo"
}
implicit def fs = new Foo[String] {
type B = Boolean
val value = false
}
好吧,在这种情况下,我们没有做任何真正的计算,我们只是根据输入类型A
改变B
的类型,这足以理解Aux
。
现在在Scala中,我们可以使用 参数依赖类型 来访问在类/特质(路径依赖类型)中定义的类型,因此如果我们想在函数中使用类型B
作为返回类型,我们可以这样做:
def foo[T](t: T)(implicit f: Foo[T]): f.B = f.value
val res1: String = foo(2)
val res2: Boolean = foo("")
在本例中,我们可以使用依赖类型和隐式参数来更改函数的返回类型,现在假设我们希望在下一个参数中使用此类型作为类型参数,例如获取该类型的Monoid实例:
import scalaz._, Scalaz._
def foo[T](t: T)(implicit f: Foo[T], m: Monoid[f.B]): f.B = m.zero
我们想这样做,但不幸的是我们得到了:
illegal dependent method type: parameter appears in the type of another parameter in the same section or an earlier one
Scala告诉我们不能在同一 section 中使用依赖类型,我们可以在下一个参数块中使用它或仅作为返回类型使用它。
这是我们的朋友Aux
将要提供帮助的地方,让我们定义它:
type Aux[A0, B0] = Foo[A0] { type B = B0 }
我们在这里所做的是定义一个类型别名,其中A0
映射到Foo A
,B0
映射到类型type B
,我一开始不明白的是关系类型B = B0
是双向的,所以如果我们用type B = Boolean
来修复类型B
,B0
也会得到这个类型。
所以现在我们可以这样写:
def foo[T, R](t: T)(implicit f: Foo.Aux[T, R], m: Monoid[R]): R = m.zero
val res1: String = foo(2)
val res2: Boolean = foo("")
一个完整的例子:
import shapeless._
import scalaz._
import Scalaz._
object console extends App {
trait Foo[A] {
type B
def value: B
}
object Foo {
type Aux[A0, B0] = Foo[A0] { type B = B0 }
implicit def fi = new Foo[Int] {
type B = String
val value = "Foo"
}
implicit def fs = new Foo[String] {
type B = Boolean
val value = false
}
}
def ciao[T, R](t: T)
(implicit f: Foo.Aux[T, R],
m: Monoid[R]): R = f.value
val res = ciao(2)
println(s"res: ${res}")
}
就是这样,基本上Aux
只是一种提取类型级计算结果的方法,现在让我们看一个现实世界的例子,我想到的最常见的例子是shapeless Generic。
def length[T, R <: HList](t: T)
(implicit g: Generic.Aux[T, R],
l: Length[R]): l.Out = l()
case class Foo(i: Int, s: String, b: Boolean)
val foo = Foo(1, "", false)
val res = length(foo)
println(s"res: ${Nat.toInt(res)}")
// res: 3
在这种情况下,Generic.Aux
将提取R
中T
的通用表示,这样我们就可以使用它来解析我们用来获取HList
长度的Length
类型类。
总结
Aux
是一种非常简单的技术,当你开始进行一些类型级编程时,它是强制性的,可能很简单,到目前为止没有人需要写一篇关于它的教程,但因为我花了一些精力来理解它,也许这可以帮助一些人。
原文 https://gigiigig.github.io/posts/2015/09/13/aux-pattern.html
文档信息
- 本文作者:梦境迷离
- 本文链接:https://blog.dreamylost.cn/scala/Scala-Aux%E6%A8%A1%E5%BC%8F%E4%BB%8B%E7%BB%8D.html
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)