在談隱含參數(Implicit parameter)之前,你可以先回憶一下 鞣製(Curry) 函式。
隱含參數搭配鞣製函式來使用,可以達到類似預設引數(Default argument)的作用,例如:
implicit val default = 1 // 也可以寫為 implicit def default = 1
def increase(a: Int)(implicit b: Int) = a + b
println(increase(10)(20)) // 30
println(increase(10)(default)) // 11
在 上例中,increase()()的第二個括號中參數被標示為implicit,在你有提供引數的情況下,會以你所提供的引數為主,所以第一個 println()顯示20的結果,而在你沒有提供第二個括號之引數情況下,編譯器會嘗試搜尋在範圍中被標示implicit的val變數或無引數函式 (這是一致性存取原則),如果找到,就直接代入,也就是上例中的第二個println(),其實編譯器會展開為:
println(increase(10)(default))
如果函式只有一個參數,也可以是隱式參數,例如:
implicit val default = "openhome.cc > "
def command(implicit prompt: String) = readLine(prompt)
val c = command
println(c)
在上例中,如果有提供command()函式引數,則以你提供的為主,若無提供,則使用預設的提示文字。
隱式參數會套用在整個參數列,而不僅是單一參數。一個例子如下所示:
implicit val p = "number > "
implicit val v = 1
def increase(implicit prompt: String, i: Int) = readLine(prompt).toInt + i
println(increase) // 相當於 println(increase(p, v))
再來看看隱式參數運用於 一級函式(First-class function) 的一個例子,例如在 上界(Upper bound)、下界(Lower bound)、視界(View bound) 中曾看過的一段程式:
def quick(list: List[Int]): List[Int] = {
list match {
case Nil => Nil
case x::xs =>
val (before,after) = xs partition (_ < x)
quick(before) ++ (x :: quick(after))
}
}
在這個例子中,是以Int來示範演算法的實作,如果要讓這個函式更一般化,可以排序任何型態的物件,則該型態必須具備 < 方法,一個方式是要求要排序的物件具有 scala.Ordered[A] 特徵。所以可以修改函式定義如下:
def quick[T <: Ordered[T]](list: List[T]): List[T] = {
list match {
case Nil => Nil
case x::xs =>
val (before,after) = xs partition (_ < x)
quick(before) ++ (x :: quick(after))
}
}
如果你定義了一個類別,具有scala.Ordered[A]特徵,則可以使用這個quick()函式來進行排序,但問題是Int、Long、Double等Int並沒有scala.Ordered[A]特徵,無法使用這個quick()函式,你可以將這個函式改寫如下:
def quick[T](list: List[T])(implicit orderer: T => Ordered[T]): List[T] = {
list match {
case Nil => Nil
case x::xs =>
val (before,after) = xs partition (orderer(_) < x)
quick(before) ++ (x :: quick(after))
}
}
你可以傳入一個函式,這個函式可以將物件轉換為具scala.Ordered[A]的物件,事實上,在scala.Predef中就定義了一些這種函式,名稱是以xxxxordered()命名,可以幫你將物件轉換為具scala.Ordered[A]的物件,在那些xxxordered()函式中,如果傳入的物件剛好具有scala.Ordered[A]特徵,則直接傳回原物件,這是因為其運用的隱式函式,會使用以下的隱式函式傳回原物件:
implicit def identity[A](x: A): A = x
事實上,如果你看看scala.Predef那些xxxordered()函式,會發現它們其實都是implicit,所以上例中,也可以改為以下的形式套用隱式轉換功能:
def quick[T](list: List[T])(implicit orderer: T => Ordered[T]): List[T] = {
list match {
case Nil => Nil
case x::xs =>
val (before,after) = xs partition (_ < x) // orderer(_) 是隱含的
quick(before) ++ (x :: quick(after))
}
}
由於這樣的函式定義結果是很常見的,所以有個縮寫的形式:
def quick[T <% Ordered[T]](list: List[T]): List[T] = {
list match {
case Nil => Nil
case x::xs =>
val (before,after) = xs partition (_ < x)
quick(before) ++ (x :: quick(after))
}
}
所以這才是 上界(Upper bound)、下界(Lower bound)、視界(View bound) 中所介紹「視界」的真正來由。