0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

什么是高階類型???如何去定義高階類型呢

jf_wN0SrCdH ? 來源:碼小菜 ? 作者:碼小菜 ? 2022-11-07 10:20 ? 次閱讀

05ad3ac8-5de9-11ed-a3b6-dac502259ad0.jpg

Rust在類型系統(tǒng)級別上與Haskell,Scala有許多相似之處。

兩者都有類型(type),泛型類型(generic types),關(guān)聯(lián)類型(associated types)和特質(zhì)/類型類(traits/type classes)(基本上是等效的)。

但是,Haskell有Rust缺乏的一個特性:高階類型(Higher Kinded Types), 也就是通常說的HKTs。

這不是Rust故意不添加,也不是Rust應該填補的一些差距。其實這是Rust的一個有意的設(shè)計。

結(jié)果就是,到?jīng)]有GATs出現(xiàn)時,某些代碼無法真正在Rust中實現(xiàn)。

以Haskell中的Functor為例。Functor其實是對類型上實現(xiàn)的map的一個更加抽象的實現(xiàn),不關(guān)心具體類型的一個接口。

比如,在Scala中,要實現(xiàn)一個map的Functor是這樣的:

importscala.language.higherKinds

traitFunctor[F[_]]{
defmap[A,B](fa:F[A])(f:A=>B):F[B]
}

如果你使用過Scala的集合api,這看起來應該非常相似。所有集合都可以使用map。

List(1,2,3).map(_+1)
//List(2,3,4)

你可能要問,什么是高階類型啊.你可以先理解為: 高階類型是類型的類型。這是什么意思呢? 我以Scala里面的代碼來說明一下。

scala>traitMyList[A]{
defhead:A
deftail:MyList[A]
}

然后用:k命令看下是什么類型:

scala>:k-vMyList
MyList'skindisF[A]
*->*
Thisisatypeconstructor:a1st-order-kindedtype.

MyList[String]類型是一個一階類型(1st-order-kinded type);任何MyList的類型都是由A參數(shù)化的,可以將MyList看作是一個類型函數(shù),如A => MyList[A],因此給定一個類型A,我們可以創(chuàng)建一個新的類型MyList[A]。如果MyList是一階類型,那么什么是類型化類型呢?其實想想,什么是高階函數(shù)啊? 它不就是一個接受函數(shù)然后返回另外另一個函數(shù)的函數(shù)。同理可得,什么是高階類型呢? 它是由另一個類型參數(shù)化的類型。我再寫一個簡單的例子。

scala>traitFoo[F[_]]:k-vFoo
Foo'skindisX[F[A]]
(*->*)->*
Thisisatypeconstructorthattakestypeconstructor(s):ahigher-kindedtype.

這里的Foo就是一個高階類型。它是一個類型構(gòu)造函數(shù),參數(shù)也是一個類型構(gòu)造函數(shù)。為了說清楚這點,我再寫一個例子.我寫一個trait,里面有一個leftFold的方法。

scala>traitFoldable[F[_]]{
|defleftFold[A,B](ob:F[A])(zero:B)(fn:(B,A)=>B):B
|}

然后呢,我再實現(xiàn)一個listFoldable, 這是最常見的吧。

scala>implicitvallistFoldable:Foldable[List]=newFoldable[List]{
|defleftFold[A,B](ob:List[A])(zero:B)(fn:(B,A)=>B):B={
|ob.foldLeft(zero)(fn)
|}
|}

上面我定義了一個適用于任何List類型的Foldable對象。leftFold方法將取A并使用A構(gòu)造List[A]。

一種更好的寫法:

scala>importscala.language.implicitConversions
importscala.language.implicitConversions

scala>implicitclassListFoldableOpt[A](list:List[A])(implicitfold:Foldable[List]){
|defleftFold[B](zero:B)(fn:(B,A)=>B):B=
|fold.leftFold(list)(zero)(fn)
|}

scala>List(1,2,3).leftFold(0)(_+_)//6

這里先收一下,回到Rust中。

Rust中的很多不同結(jié)構(gòu)都實現(xiàn)了map函數(shù),比如: Option, Result, Iterator, and Future.

但是, 在Rust里面,還不能編寫可以給多種類型實現(xiàn)的通用Functortrait, 就像我剛才上面寫的trait Functor[F[_]]。通常呢 在Rust里面是單個類型單獨去實現(xiàn)map。例如,我這里自己寫了一個MyOption和MyResult枚舉類, 并實現(xiàn)了map方法.

#[derive(Debug,PartialEq)]
enumMyOption{
Some(A),
None,
}

implMyOption{
fnmapB,B>(self,f:F)->MyOption{
matchself{
MyOption::Some(a)=>MyOption::Some(f(a)),
MyOption::None=>MyOption::None,
}
}
}

#[test]
fntest_option_map(){
assert_eq!(MyOption::Some(5).map(|x|x+1),MyOption::Some(6));
assert_eq!(MyOption::None.map(|x:i32|x+1),MyOption::None);
}

#[derive(Debug,PartialEq)]
enumMyResult{
Ok(A),
Err(E),
}

implMyResult{
fnmapB,B>(self,f:F)->MyResult{
matchself{
MyResult::Ok(a)=>MyResult::Ok(f(a)),
MyResult::Err(e)=>MyResult::Err(e),
}
}
}

#[test]
fntest_result_map(){
assert_eq!(MyResult::Ok(5).map(|x|x+1),MyResult::(6));
assert_eq!(MyResult::Err("hello").map(|x:i32|x+1),MyResult::Err("hello"));
}

上面的幾個例子都是直接在自己的結(jié)構(gòu)中定義的map方法。如果沒有GATs, 就不可能將map定義為一個trait的方法。這是為什么呢?下面是一個在Rust里面簡單的Functortrait實現(xiàn),以及Option的實現(xiàn).

traitNaiveFunctor{
typeT;
fnmap(self,f:F)->Self
where
F:FnMut(Self::T)->Self::T;
}

implNaiveFunctorforOption{
typeT=A;
fnmapA>(self,mutf:F)->Option{
matchself{
Some(a)=>Some(f(a)),
None=>None,
}
}
}

在上面的trait定義中,先給NaiveFunctor內(nèi)部的值定義了一個關(guān)聯(lián)類型T。給Optionimpl這個trait,T=A。這就是問題所在。Rust將T硬編碼為一種類型A,因為通常在map函數(shù)中,希望返回的類型是B,但在之前版本穩(wěn)定的Rust中,沒有辦法說"我想要一個既能與NaiveFunctor關(guān)聯(lián)的類型,又要求這個類型和關(guān)聯(lián)類型有一點不一樣。

這就是泛型關(guān)聯(lián)類型發(fā)揮作用的地方了。

多態(tài)Functor的實現(xiàn)

為了得到一個多態(tài)Functor.我想要的是:"我的類型最終應該是我在其中包裹的類型".例如,對于Option,我想說的是"如果我有一個Option,那么它肯定包含一個A類型,但如果它包含一個B類型,它將是Option"

這里就需要使用泛型關(guān)聯(lián)類型.

traitFunctor{
typeUnwrapped;
typeWrapped:Functor;
fnmap(self,f:F)->Self::Wrapped
where
F:FnMut(Self::Unwrapped)->B;
}

說明下上面的寫法:

每個Functor都有一個關(guān)聯(lián)的Unwrapped類型.

還有另一個關(guān)聯(lián)類型Wrapped,它與Self類似,但有一個不同的包裝值

和前面例子一樣, map接受兩個參數(shù)的一個是:self和一個函數(shù)

函數(shù)形參將從當前關(guān)聯(lián)的Unwrapped值映射到一個新的類型B

map的輸出是一個Wrapped

有點抽象。具體點現(xiàn)在看下Option類型是什么樣子的

implFunctorforOption{
typeUnwrapped=A;
typeWrapped=Option;

fnmapB,B>(self,mutf:F)->OptionwhereF:FnMut(A)->B{
matchself{
Some(x)=>Some(f(x)),
None=>None,
}
}
}

#[test]
fntest_option_map(){
assert_eq!(Some(5).map(|x|x+1),Some(6));
assert_eq!(None.map(|x:i32|x+1),None);
}

編譯通過,非常優(yōu)雅。

類型類

在Haskell和Scala中,其實是不需要這種泛型關(guān)聯(lián)類型的。事實上,Haskell中Functors不使用任何關(guān)聯(lián)類型。Haskell中Functor的類型類遠遠早于其他語言中關(guān)聯(lián)類型的出現(xiàn)。為了進行比較,先看看它是什么樣子。

classFunctorfwhere
map::(a->b)->fa->fb

instanceFunctorOptionwhere
mapfoption=
caseoptionof
Somex->Some(fx)
None->None
traitFunctor[F[_]]{
defmap[A,B](fa:F[A])(f:A=>B):F[B]
}

把它轉(zhuǎn)換成Rust就是如下:

traitHktFunctor{
fnmapB>(self:Self,f:F)->Self;
}

implHktFunctorforOption{
fnmapB>(self:Option,f:F)->Option{
matchself{
Some(a)=>Some(f(a)),
None=>None,
}
}
}

但是上面的代碼是編譯不通過的。因為我試圖給Self提供類型參數(shù)。但是在Rust中, 單獨一個Option不是一個類型. Option要成為一個類型,必須有一個類型參數(shù).比如: Option 是一個類型. Option卻不是.

相反,在Haskell中,Maybe Int是kind type的一種類型。Maybe是類型構(gòu)造函數(shù),類型為type -> type。但是出于創(chuàng)建類型類和實例的目的,可以將Maybe處理為具有自己的類型。Haskell中的Functor作用于type->type。

這就是我所說的"高階類型": 就是說我可以寫出擁有類型的類型(Kind)。

Pointer 的實現(xiàn)

Haskell中有一個有爭議的類型類叫做Pointed。之所以有爭議的,是因為它引入了一個類型類,但沒有與它相關(guān)的任何laws。我想在Rust中實現(xiàn)下Pointed。

什么是Pointed

Pointed的思想很簡單:將一個值包裝成一個類似Functor的東西。比如:在Option的情況下,它就用Some包裝它。在Result中,它使用Ok。

對于Vec,它是一個單值向量。與Functor不同,這里是一個靜態(tài)方法,因為我們沒有現(xiàn)有的Point值要改。讓我們看看它在Rust中的實現(xiàn)。

traitPointed:Functor{
fnwrap(t:T)->Self::Wrapped;
}

implPointedforOption{
fnwrap(t:T)->Self::Wrapped{
Some(t)
}
}

這里需要注意的是: 我根本沒有在Option實現(xiàn)中使用A類型參數(shù)。

還有一點值得注意。調(diào)用wrap的結(jié)果返回的是Self::Wrapped。關(guān)于Self::Wrapped,到底是什么?從之前Functor的定義中,確切地知道一件事:Wrapped必須是一個Functor的。








審核編輯:劉清

  • GAT
    GAT
    +關(guān)注

    關(guān)注

    0

    文章

    7

    瀏覽量

    6326
  • API接口
    +關(guān)注

    關(guān)注

    1

    文章

    82

    瀏覽量

    10420
  • Rust
    +關(guān)注

    關(guān)注

    1

    文章

    228

    瀏覽量

    6547

原文標題:Rust中GAT和高階類型

文章出處:【微信號:Rust語言中文社區(qū),微信公眾號:Rust語言中文社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    高階濾波器的設(shè)計

    高階濾波器的設(shè)計 實現(xiàn)高階濾波器的方法是把基高階函數(shù)分解成多個二階因式之積,每個二階因式用對應的二階濾波器來實現(xiàn),將這些二階濾波器串接起來
    發(fā)表于 05-23 15:39 ?7118次閱讀
    <b class='flag-5'>高階</b>濾波器的設(shè)計

    高階程控濾波器的研究

    高階程控濾波器的研究作者:李鳴華關(guān)鍵詞:巴氏濾波網(wǎng)絡,高階程控濾波器,儀表測試摘要:本文提出了一種基于二階巴氏濾波網(wǎng)絡的高階程控濾波器的實現(xiàn)原理與方法,在儀表測試及工程應用領(lǐng)域有一定的實用價值。濾波
    發(fā)表于 12-05 09:08

    高階系統(tǒng)的時域分析

    高階系統(tǒng)的時域分析
    發(fā)表于 04-10 20:58

    SAR與ADC不同輸入類型

    作者: Amit Kumbasi 今天,我們繼續(xù)講解與逐次逼近寄存器 (SAR) 數(shù)模轉(zhuǎn)換器 (ADC) 輸入類型有關(guān)的內(nèi)容。在之前的部分中,我研究了輸入注意事項和SAR ADC之間的性能比較。在這
    發(fā)表于 09-11 14:49

    怎樣設(shè)計一個高階帶阻濾波器

    為什么網(wǎng)上高階的帶通濾波器比比皆是,而帶阻濾波器只有最高兩階的?如果有高階的(最好是四階的)帶阻濾波器請發(fā)個設(shè)計圖 我想學習學習
    發(fā)表于 06-09 18:20

    什么是高階函數(shù)?

    高階函數(shù)是將其他函數(shù)作為形參,或者以函數(shù)作為返回結(jié)果。因為在Scala中,函數(shù)是***。這個術(shù)語可能聽起來有點亂,但實際上我們把 以函數(shù)作為形參或以函數(shù)作為返回結(jié)果的函數(shù)和方法統(tǒng)稱為高階函數(shù)。
    發(fā)表于 11-05 06:46

    如何實現(xiàn)一種環(huán)路濾波器(高階)參數(shù)的設(shè)計?

    數(shù)字鎖相環(huán)是什么?造成PLL異常鎖定的原因是什么?如何實現(xiàn)一種環(huán)路濾波器(高階)參數(shù)的設(shè)計?
    發(fā)表于 06-22 06:53

    怎樣使用typedef定義一個數(shù)據(jù)類型

    typedef與#define有哪些不同?怎樣使用typedef定義一個數(shù)據(jù)類型?
    發(fā)表于 02-25 07:56

    HLS高階綜合的定義及挑戰(zhàn)

    HLS高階綜合(high level synthesis)在被廣泛使用之前,作為商業(yè)技術(shù)其實已經(jīng)存在了20多年。設(shè)計團隊對于這項技術(shù)可以說呈現(xiàn)出兩極化的態(tài)度:要么堅信它是先進技術(shù)之翹楚,要么對其持謹慎懷疑態(tài)度。
    發(fā)表于 11-04 13:45 ?3323次閱讀

    基于線性網(wǎng)格創(chuàng)建高階網(wǎng)格

    在 CFD 模擬使用的多種網(wǎng)格生成方法中,高階網(wǎng)格是一種能夠?qū)崿F(xiàn)精度、分辨率和計算成本平衡的有效方法。高階網(wǎng)格劃分的目標是利用高階多項式曲線的優(yōu)勢為 CFD 計算創(chuàng)建網(wǎng)格,從而實現(xiàn)在復雜系統(tǒng)環(huán)境下提供比線性網(wǎng)格更高的精度。
    的頭像 發(fā)表于 09-22 10:30 ?907次閱讀

    Rust中GAT和高階類型

    Rust在類型系統(tǒng)級別上與Haskell,Scala有許多相似之處。
    的頭像 發(fā)表于 11-07 10:21 ?1136次閱讀

    用戶自定義類型

    SystemVerilog還為工程師定義新的數(shù)據(jù)類型提供了一種機制。用戶定義的數(shù)據(jù)類型允許從現(xiàn)有數(shù)據(jù)類型創(chuàng)建新的
    的頭像 發(fā)表于 02-09 14:53 ?1019次閱讀
    用戶自<b class='flag-5'>定義</b><b class='flag-5'>類型</b>

    光腔的損耗有哪些類型

    取決于腔的類型和幾何尺寸。幾何損耗的高低依模式的不同而異,高階橫模損耗大于低階橫模損耗。是非穩(wěn)腔的主要損耗。
    的頭像 發(fā)表于 02-25 15:55 ?1024次閱讀

    定義數(shù)據(jù)類型

    在運算之前我們必須首先定義出數(shù)據(jù)類型,定義出腳本支持的數(shù)據(jù)類型,這是運算的基礎(chǔ)。 這一小節(jié)我們將定義出數(shù)據(jù)
    的頭像 發(fā)表于 03-03 10:10 ?894次閱讀

    UHDI及高階封裝技術(shù)對檢測系統(tǒng)的挑戰(zhàn)

    高階封裝技術(shù)相關(guān)的復雜性增加使含有多種芯片類型及小型化元器件的PCB設(shè)計更復雜。此外,在2.5D和3D封裝等高階封裝解決方案的推動下,行業(yè)朝著更高密度和更小間距的方向發(fā)展,對檢測設(shè)備提出了顯著需求。
    發(fā)表于 10-23 15:16 ?390次閱讀
    UHDI及<b class='flag-5'>高階</b>封裝技術(shù)對檢測系統(tǒng)的挑戰(zhàn)