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

原创
2016/06/13 15:39
阅读数 3.8K

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

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

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

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

高级类类型可以用于创建复杂类型,如M[N[T,X],Y],但可以看作更简单的类型,F[X]。

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
}

现在,我们分别为不同的类型实现该特质:

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 = ""
}

现在,我们要为这个抽象的类类型,实现抽象的方法:

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

它接收所有集合类型,现在我们为其实现具体的实现:

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框架便由此诞生了!!

展开阅读全文
加载中

作者的其它热门文章

打赏
0
1 收藏
分享
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部