scala片段4:使用型別類實現Pimp my library模式
原文來自:
Scala snippets 4: Pimp my library pattern with type classes.
Posted by: Jos Dirksen
January 16th, 2015
轉截請註明來自圖靈社群
fairjm
我想寫一篇關於scalaz
有趣的部分的文章,但最好還是先看一下scala提供的型別系統。所以在這個片段中,我們將探索一個很小的部分,關於型別類是如何工作並且幫助你寫出更通用的程式碼。
你能找到更多的片段:
Scala snippets 1: Folding
Scala snippets 2: List symbol magic
Scala snippets 3: Lists together with Map, flatmap, zip and reduce
Scala snippets 4: Pimp my library pattern with type classes
注:前三個片段圖靈社群也有相關的翻譯
型別類
看到關於型別類在維基百科的定義可能會馬上嚇跑你:
“In computer science, a type class is a type system construct that supports ad hoc polymorphism. This is achieved by adding constraints to type variables in parametrically polymorphic types. Such a constraint typically involves a type class ‘T’ and a type variable ‘a’, and means that ‘a’ can only be instantiated to a type whose members support the overloaded operations associated with ‘T’.”
總的來說型別類允許向現有的類新增新功能但不需要修改現有的類。比如說向String
新增標準的"可比較(comparable)"功能但不需要改動現有的類。注意你也可以只用隱式函式來增加自定義的行為(比如"Pimp my library pattern",但是使用型別類更加地安全和靈活。關於這個的一個很好的討論在這裡。
介紹以及足夠了,讓我們來看一個非常簡單的型別類的例子。在scala中建立一個型別類需要幾步。
第一步是建立一個特質。這個特質是實際的型別類並且定義了我們想要實現的功能。在這篇文章中,我們將要建立一個很做作的例子,定義一個Duplicate
特質。這個特質的功能是重複特定的物件,當傳入"hello"
時,預期的返回值是"hellohello"
。當引數是個整數時,我們返回value * value
,當得到一個字元c
時,我們返回"cc"
。所有的這些都以型別安全的方式實現。
我們的型別類實際上非常簡單:
trait Duplicate[A,B] {
def duplicate(value: A): B
}
注意,它看上去很像scala的mix-in
特質,但實際上使用方式完全不一樣。一旦我們得到了型別類的定義,接下去的步驟就是建立一些預設的實現。我們將這些寫在特質的伴生物件內:
object Duplicate {
// implemented as a singleton object
implicit object DuplicateString extends Duplicate[String,String] {
def duplicate(value: String) = value.concat(value)
}
// or directly, which I like better.
implicit val duplicateInt = new Duplicate[Int, Int] {
def duplicate(value: Int) = value * value
}
implicit val duplicateChar = new Duplicate[Char, String] {
def duplicate(value: Char) = value.toString + value.toString
}
}
}
通過上面的程式碼可以看到我們能用幾種不同的方式來實現。其中最重要的是implicit
這個關鍵字。使用這個關鍵字我們可以讓這些成員在特定的情況下被隱含使用。這些實現都很直接,我們只是用已經定義了的特定的型別(String
,Int
和Char
)來實現特質。
現在我們開始使用型別類:
object DuplicateWriter {
// import the conversions for use within this object
import conversions.Duplicate
// Generic method that takes a value, and looks for an implicit
// conversion of type Duplicate. If no implicit Duplicate is available
// an error will be thrown. Scala will first look in the local
// scope before looking for implicits in the companion object
// of the trait class.
def write[A,B](value: A)(implicit dup: Duplicate[A, B]) : B = {
dup.duplicate(value)
}
}
// simple app that runs our conversions
object Example extends App {
import snippets.conversions.Duplicate
implicit val anotherDuplicateInt = new Duplicate[Int, Int] {
def duplicate(value: Int) = value + value
}
println(DuplicateWriter.write("Hello"))
println(DuplicateWriter.write('c'))
println(DuplicateWriter.write(0))
println(DuplicateWriter.write(0)(Duplicate.duplicateInt))
}
在這個例子中,我們建立了一個DuplicateWriter
來呼叫與給定的引數型別匹配的duplicate函式。在給的例子中,我們也用了一個Int
型別的其他實現來替代預設的實現(最後一行的程式碼)。 輸出的結果:
20
100
HelloHello
cc
注:這裡有些錯誤 首先程式碼裡的0 應該是10,其次程式碼的列印順序不對(或者是輸出順序不對)..
如果我們用一個不支援的型別(比如Double
):
println(DuplicateWriter.write(0d))
我們將得到一個編譯期的錯誤:
Error:(56, 32) could not find implicit value for parameter dup: snippets.conversions.Duplicate[Double,B]
println(DuplicateWriter.write(0d))
^
Error:(56, 32) not enough arguments for method write: (implicit dup: snippets.conversions.Duplicate[Double,B])B.
Unspecified value parameter dup.
println(DuplicateWriter.write(0d))
^
我們可以定製化第一個錯誤資訊在我們的特質上:
@implicitNotFound("No member of type class Duplicate in scope for ${T}")
trait Duplicate[A,B] {
def duplicate(value: A): B
}
這是一個很快的關於型別類的介紹。你可以看到,它們提供了很簡單的方法來對已有的類增加自定義的功能,你甚至不需要能觸及這些類。在下一個片段中,我們將探索一些在scalaz
庫中的普遍的,非常有用的型別類。
原文來自:
Scala snippets 4: Pimp my library pattern with type classes.
Posted by: Jos Dirksen
January 16th, 2015
轉截請註明來自圖靈社群
fairjm
相關文章
- scala:分別使用懶漢式和餓漢式實現單例模式單例模式
- Scala型別類的小應用之Functor Foldable型別LDA
- Scala由類的動態擴充套件想到型別類套件型別
- [譯] Scala 型別的型別(四)型別
- [譯] Scala 型別的型別(二)型別
- [譯] Scala 型別的型別(三)型別
- [譯] Scala 型別的型別(六)型別
- [譯] Scala 型別的型別(五)型別
- Mac有趣圖片編輯軟體——Pimp My Picture for MacMac
- scala_繼承、型別判斷、抽象類、匿名內部類繼承型別抽象
- scala中的匿名子類實現
- Scala 泛型型別和方法泛型型別
- Scala(一)資料型別資料型別
- 【Scala之旅】型別引數型別
- 探索Scala(5)-- 基本型別型別
- Scala結構型別與複合型別解析型別
- java中介面多個實現類,如何指定實現類,根據子類型別選擇實現方法Java型別
- 在 Kotlin 中“實現”trait/型別類KotlinAI型別
- 【Scala篇】--Scala中Trait、模式匹配、樣例類、Actor模型AI模式模型
- Scala 片段2:List的操作符魔法
- 用委託者模式實現的多型別Adapter模式多型型別APT
- 使用Resilience4J實現斷路器模式模式
- Go型別嵌入介紹和使用型別嵌入模擬實現“繼承”Go型別繼承
- 【Scala之旅】特質與高階型別型別
- scala和java資料型別轉換Java資料型別
- 泛型類、泛型方法、型別萬用字元的使用泛型型別字元
- js map型別實現JS型別
- Scala 中的集合(三):實現一個新的 Collection 類
- Scala 中的集合(一):集合型別與操作型別
- 【型別轉換】使用c#實現簡易的型別轉換(Emit,Expression,反射)型別C#MITExpress反射
- 使用陣列實現環形佇列Scala版本陣列佇列
- Scala模式匹配模式
- Scala 片段3:列表的map,flatMap,zip和reduce
- Scala實現乘法口訣
- 用Scala macros實現DCIMacROS
- 第11章 使用類——型別轉換(二)將自定義型別轉換為內建型別型別
- Scala Learn 5 模式匹配和樣例類 (待補充)模式
- Scala學習(五)---Scala中的類