Aux模式介绍

博客

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 AB0映射到类型type B,我一开始不明白的是关系类型B = B0 是双向的,所以如果我们用type B = Boolean来修复类型BB0也会得到这个类型。

所以现在我们可以这样写:

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将提取RT的通用表示,这样我们就可以使用它来解析我们用来获取HList长度的Length类型类。

总结

Aux是一种非常简单的技术,当你开始进行一些类型级编程时,它是强制性的,可能很简单,到目前为止没有人需要写一篇关于它的教程,但因为我花了一些精力来理解它,也许这可以帮助一些人。

原文 https://gigiigig.github.io/posts/2015/09/13/aux-pattern.html

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值