Scala类型系统——高级类类型(higher-kinded types)

高级类类型就是使用其余类型构形成为一个新的类型,所以也称为 类型构造器(type constructors)。它的语法和高阶函数(higher-order functions)类似,高阶函数就是将其它函数做为参数的函数;高级类类型则是将构造类类型做为参数类型。一个高级类类型能够有一个或者多个类型做为参数。在Scala中,你可使用type关键字声明,以下:数组

type Callback[T] = Function1[T,Unit]

这里定义了一个高级类类型Callback,该类型接收一个类型 T,并构造一个新的类型Function1。类型Callback不是一个完整的类型,直到它实现了参数化。app

Function0,Function1,... 表示接收类型个数1,2,3,... ,如Function0[A],Function1[A,B]... ,一共23个FunctionX。用于定义匿名语法,实现语法糖。框架

高级类类型能够用于建立复杂类型,如M[N[T,X],Y],但能够看做更简单的类型,F[X]。ide

Higher-kinded types

除了上面说的用关键字type定义的类型外,高级类类型最多见的实现是使用占位符代替。即F[_],在Scala中,占位符 _ 有特别的意义,能够表示任何东西。函数

User Story

如咱们要实现一个sum方法,该方法能够接收任何Scala集合类型,并实现加法处理。咱们可能实现的一种方式是,为全部集合类型定义相对应的方法:单元测试

def sumList(xs: List[Int]): Int = xs.foldLeft(0)(_ + _)
def sumArray(xs: Array[Int]): Int = xs.foldLeft(0)(_ + _)

但这并非一个高效的函数实现,咱们要对全部集合类型进行抽象,以使得sum方法能够接收任何集合类型。所以,咱们要实现高级类类型。测试

trait Summable[A] {
  def plus(a1: A, a2: A): A
  def init: A
}

如今,咱们分别为不一样的类型实现该特质:scala

object IntSummable extends Summable[Int] {
  override def plus(a1: Int, a2: Int): Int = a1 + a2
  override def init: Int = 0
}

object StringSummable extends Summable[String] {
  override def plus(a1: String, a2: String): String = a1 + a2
  override def init: String = ""
}

如今,咱们要为这个抽象的类类型,实现抽象的方法:code

trait Foldable[F[_]] {
  def foldLeft[A](xs: F[A], m: Summable[A]): A
}

它接收全部集合类型,如今咱们为其实现具体的实现:three

object ListFoldLeft extends Foldable[List] {
  override def foldLeft[A](xs: List[A], m: Summable[A]): A = xs.foldLeft(m.init)(m.plus)
}
object ArrayFoldLeft extends Foldable[Array] {
  override def foldLeft[A](xs: Array[A], m: Summable[A]): A = xs.foldLeft(m.init)(m.plus)
}

根据这个特质,咱们如今能够实现其通用的函数sum,该函数接收三个参数:集合、Foldable特质、Summable特质,以下:

def sum[F[_], A](xs: F[A], f: Foldable[F], m: Summable[A]): A = f.foldLeft(xs, m)

执行这个方法,打印输出:

logger.info(s"${sum(List(1, 2, 3), ListFoldLeft, IntSummable)}")
logger.info(s"${sum(Array("one", "two", "three"), ArrayFoldLeft, StringSummable)}")

好了,咱们的高级类类型就已经实现了。可是等等,上面这个函数看起来很死板,我为何要接收三个参数呢,有没有sum(List(1,2,,3)) sum(Array("one", "two", "three"))这样的实现?有!

Scala有个强大的特性就是隐式调用,咱们须要实现这个sum函数的隐式调用,修改以下:

implicit val ListFoldable = new Foldable[List]{
  override def foldLeft[A](xs: List[A], m: Summable[A]): A = xs.foldLeft(m.init)(m.plus)
}

implicit val ArrayFoldable = new Foldable[Array] {
  override def foldLeft[A](xs: Array[A], m: Summable[A]): A = xs.foldLeft(m.init)(m.plus)
}

implicit val IntSum = new Summable[Int] {
  override def plus(a1: Int, a2: Int): Int = a1 + a2
  override def init: Int = 0
}

implicit val StringSum = new Summable[String] {
  override def plus(a1: String, a2: String): String = a1 + a2
  override def init: String = ""
}

上面的写法实际上和下面是同价的:

implicit object ListFoldLeft extends Foldable[List] {
  override def foldLeft[A](xs: List[A], m: Summable[A]): A = xs.foldLeft(m.init)(m.plus)
}

修改上述sum函数,让其只接收一个参数:

def sum[F[_]:Foldable,A:Summable](xs:F[A]) = {
  val c = implicitly[Foldable[F]]
  val d = implicitly[Summable[A]]
  c.foldLeft(xs,d)
}
logger.info(s"${sum(List(1,2,3))}")
logger.info(s"${sum(Array("one","two","three"))}")

SBT控制台执行上述代码,即可以获得相同的结果。 单元测试

Scala的高级类类型颇有用,而且用途普遍,下面咱们看看一个Java Servlet到Scala Servlet的一个转换框架Scalaz。

Scalaz核心实现

Scalaz的核心部分很简单,该框架的核心就是实现了全部HTTP请求和响应的抽象。即,把全部的HTTP请求抽象为一个Request特质,全部响应也抽象为一个Response特质。咱们看看这个抽象实现的核心代码:

object Application {
  def application[IN[_], OUT[_]](f: Request[IN] => Response[OUT])
     = new Application[IN,OUT] {
       def apply(implicit req: Request[IN]) = f(req)
  }
}

没错,这里的Application,对应Java Servlet里面的application,其中IN[]和OUT[]的实现为Request[IN] => Response[OUT],即抽象请求处理后,返回抽象响应。这个方法实现了高级类类型(higher-kinded types),该方法实现了输入到输出的全部转换,这里的f函数为一个高阶函数实现。

经过高级类类型,结合高阶函数,函数组合,并结合相应的隐式转换和语法糖,Scalaz框架便由此诞生了!!

相关文章
相关标签/搜索