日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Scala的Higher-Kinded类型

發布時間:2024/2/28 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Scala的Higher-Kinded类型 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Scala的Higher-Kinded類型

Higher-Kinded從字面意思上看是更高級的分類,也就是更高一級的抽象。我們先看個例子。

如果我們要在scala中實現一個對Seq[Int]的sum方法,應該怎么做呢?

def sum(seq: Seq[Int]): Int = seq reduce (_ + _)sum(Vector(1,2,3,4,5)) // 結果值: 15

看起來很簡單,剛剛我們實現了Seq[Int]的sum操作,那么如果我們想更進一步,我們想同時實現Seq[Int]和Seq[(Int,Int)]的操作該怎么處理呢?

不同的Seq需要不同的add實現,我們先抽象一個trait:

trait Add[T] { def add(t1: T, T2: T): T }

接下來我們在Add的伴生類中定義兩個隱式實例,一個Add[Int], 一個Add[(Int,Int)]。

object Add { implicit val addInt = new Add[Int] { def add(i1: Int, i2: Int): Int = i1 + i2 } implicit val addIntIntPair = new Add[(Int,Int)] { def add(p1: (Int,Int), p2: (Int,Int)): (Int,Int) = (p1._1 + p2._1, p1._2 + p2._2) } }

這兩個隱式實例分別為Add[Int], 一個Add[(Int,Int)]實現了add方法。

最后我們可以定義sumseq方法了:

def sumSeq[T : Add](seq: Seq[T]): T = seq reduce (implicitly[Add[T]].add(_,_))

T : Add 被稱為 上下文定界( context bound), 它暗指隱式參數列表將接受Add[T] 實例。

我們看下怎么調用:

sumSeq(Vector(1 -> 10, 2 -> 20, 3 -> 30)) // 結果值: (6,60) sumSeq(1 to 10) // 結果值: 55 sumSeq(Option(2)) // 出錯!

sumSeq可以接受Seq[Int]和Seq[(Int,Int)]類型,但是無法接收Option。

對于任何一種序列,只要我們為它定義了隱式的Add 實例,那么sumSeq 方法便能計算出該序列的總和。

不過,sumSeq 仍然只支持Seq 子類型。假如容器類型并不是Seq 子類型,但是卻實現了reduce 方法,我們該如何對該容器進行處理呢?我們會使用更加泛化的求和操作。這時候就需要使用到higher-kinded 類型了。

我們用M替代Seq,則可以得到M[T],M[T]就是本文介紹的Higher-Kinded類型。

trait Reduce[T, -M[T]] { def reduce(m: M[T])(f: (T, T) => T): T } object Reduce { implicit def seqReduce[T] = new Reduce[T, Seq] { def reduce(seq: Seq[T])(f: (T, T) => T): T = seq reduce f } implicit def optionReduce[T] = new Reduce[T, Option] { def reduce(opt: Option[T])(f: (T, T) => T): T = opt reduce f } }

為了能對Seq 和Option 值執行reduce操作,我們分別為這兩類類型提供了隱式實例。為了簡化起見,我們將直接使用類型中已經提供的reduce 方法執行reduce操作。

注意這里-M[T]是逆變類型,還記得我們之前的結論嗎?函數的參數一定是逆變類型的。 因為M[T]是reduce(m: M[T])的參數,所以我們需要定義它為逆變類型-M[T]。

我們看一下sum方法該怎么定義:

def sum[T : Add, M[T]](container: M[T])( implicit red: Reduce[T,M]): T = red.reduce(container)(implicitly[Add[T]].add(_,_))

調用結果如下:

sum(Vector(1 -> 10, 2 -> 20, 3 -> 30)) // 結果值: (6,60) sum(1 to 10) // 結果值: 55 sum(Option(2)) // 結果值: 2 sum[Int,Option](None) // 錯誤!

最后一個調用,我們為sum 方法添加的類型簽名[Int, Opton] 會要求編譯器將None 解釋成Option[Int] 類型。假如不添加該類型簽名,我們將得到編譯錯誤:無法判斷Option[T] 類型中的類型參數T 到底應該對應addInt 方法還是addIntIntPair 方法。

通過顯式地指定類型,我們能夠得到真正希望捕獲的運行錯誤:我們不能對None 值調用reduce 方法。

在上面的sum方法中,sum[T : Add, M[T]], T: Add是上下文邊界,我們也想定義M[T] 的上下文邊界,比如M[T] : Reduce。
因為上下文邊界只適用于包含單參數的場景,而Reduce 特征包
含兩個類型參數,所以我們需要對Reduce進行改造:

trait Reduce1[-M[_]] { def reduce[T](m: M[T])(f: (T, T) => T): T } object Reduce1 { implicit val seqReduce = new Reduce1[Seq] { def reduce[T](seq: Seq[T])(f: (T, T) => T): T = seq reduce f } implicit val optionReduce = new Reduce1[Option] { def reduce[T](opt: Option[T])(f: (T, T) => T): T = opt reduce f } }

在新的reduce1中,只包含一個類型參數且屬于higher-kinded 類型。

M[_]是上篇文章我們講到的存在類型。T 參數被移至reduce 方法。

修改后的sum方法如下:

def sum[T : Add, M[_] : Reduce1](container: M[T]): T = implicitly[Reduce1[M]].reduce(container)(implicitly[Add[T]].add(_,_))

我們定義了兩個上下文邊界,它們分別作用于Reduce1 和Add。而使用implicity 修飾的類型參數則能夠區分出這兩種不同的隱式值。

M[_]就是我們經常會看到的higher-kinded, higher-kinded雖然帶給我們額外的抽象,但是使代碼變得更加復雜。大家可以酌情考慮是否需要使用。

更多精彩內容且看:

  • 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
  • Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
  • Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
  • java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程

更多教程請參考 flydean的博客

總結

以上是生活随笔為你收集整理的Scala的Higher-Kinded类型的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。