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

相關文章
相關標籤/搜索