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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Scalaz(27)- Inference Unapply :类型的推导和匹配

發(fā)布時間:2024/9/20 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Scalaz(27)- Inference Unapply :类型的推导和匹配 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

??經(jīng)過一段時間的摸索,用scala進行函數(shù)式編程的過程對我來說就好像是想著法兒如何將函數(shù)的款式對齊以及如何正確地匹配類型,真正是一種全新的體驗,但好像有點太偏重學(xué)術(shù)型了。 本來不想花什么功夫在scala的類型系統(tǒng)上,但在閱讀scalaz源代碼時往往遇到類型層面的編程(type level programming),常常擾亂了理解scalaz代碼思路,所以還是要簡單的介紹一下scala類型系統(tǒng)的一些情況。scala類型系統(tǒng)在scala語言教材中一般都提及到了。但有些特殊的類型如phantom type, dependent type等,以及在一些場合下使用類型的特殊技巧還是值得研究的。scala類型系統(tǒng)的主要功能就是在程序運行之前,在編譯時(compile time)盡量捕捉代碼中可能出現(xiàn)的錯誤,也就是類型不匹配錯誤。scala類型系統(tǒng)是通過找尋隱式轉(zhuǎn)換類型證例(implicit type evidence)來判斷代碼中當(dāng)前類型是否期待的類型從而確定是否發(fā)生類型錯誤(type error)。寫起來很簡單,我們只要用隱式參數(shù)(implicit parameter)來表述一個隱式的類型實例(implicit type instance):


1 trait Proof 2 def sayHi(implicit isthere: Proof) = println("hello") 3 sayHi //編譯失敗
創(chuàng)建一個Proof實例后:


1 trait Proof 2 def sayHi(implicit isthere: Proof) = println("hello") 3 //> sayHi: (implicit isthere: Exercises.deptype.Proof)Unit 4 implicit object ishere extends Proof //建一個實例 5 sayHi


sayHi現(xiàn)在能正常通過編譯了。雖然在sayHi函數(shù)內(nèi)部并沒有引用這個隱式參數(shù)isthere,但這個例子可以說明編譯器進行類型推斷的原理。一般來說我們都會在函數(shù)內(nèi)部引用isthere這種隱式參數(shù),并且按不同需要在隱式轉(zhuǎn)換解析域內(nèi)創(chuàng)建不同功能的類型實例(instance):

1 trait Proof { def apply(): String} 2 def sayHi(implicit isthere: Proof) = println(isthere()) 3 //> sayHi: (implicit isthere: Exercises.deptype.Proof)Unit 4 implicit object ishere extends Proof {def apply() = "Hello World!"} 5 sayHi //> Hello World!


在Scalaz中還有些更復(fù)雜的引用例子如:scalaz/BindSyntax.scala

def join[B](implicit ev: A <~< F[B]): F[B] = F.bind(self)(ev(_))
1 List(List(1),List(2),List(3)).join //> res0: List[Int] = List(1, 2, 3) 2 //List(1.some,2.some,3.some).join //無法編譯,輸入類型不對
以上例子里的隱式轉(zhuǎn)換和解析域就比較隱晦了:scalaz/Liskov.Scala

trait LiskovFunctions {import Liskov._/**Lift Scala's subtyping relationship */implicit def isa[A, B >: A]: A <~< B = new (A <~< B) {def subst[F[-_]](p: F[B]): F[A] = p}


這個隱式轉(zhuǎn)換產(chǎn)生的實例限定了A必須是B或者是B的子類。在這個例子中不但限定了類型的正確性,而且還進行了些類型關(guān)系的推導(dǎo)。理論上我們可以用依賴類型(dependent type)來描述類型參數(shù)之間的關(guān)系,推導(dǎo)結(jié)果類型最終確定代碼中類型的正確無誤。據(jù)我所知scala并不支持完整功能的依賴類型,但有些前輩在scala類型編程(type level programming)中使用了一些依賴類型的功能和技巧。Scalaz的unapply就利用了依賴類型的原理,然后通過隱式參數(shù)(implicit parameter)證明某些類型實例的存在來判斷輸入?yún)?shù)類型正確性的。Unapply的構(gòu)思是由Miles Sabin創(chuàng)造的。我們先用他舉的一個例子來看看如何利用依賴類型及類型實例通過隱式輸入?yún)?shù)類型來推導(dǎo)結(jié)果類型并判斷輸入?yún)?shù)類型正確性的:

1 trait TypeA2 trait TypeB3 4 trait DepType[A,B,C] //依賴類型5 implicit object abb extends DepType[TypeA,TypeB,TypeB] {6 def apply(a:TypeA, b:TypeB): TypeB = error("TODO") //結(jié)果類型依賴TypeA和TypeB7 }8 implicit object aaa extends DepType[TypeA,TypeA,TypeA] {9 def apply(a:TypeA, b:TypeA): TypeA = error("TODO") //結(jié)果類型依賴TypeA和TypeA 10 } 11 implicit object iab extends DepType[Int,TypeA,TypeB] { 12 def apply(a:Int, b:TypeA): TypeB = error("TODO") //結(jié)果類型依賴Int和TypeB 13 } 14 implicit object bbi extends DepType[TypeB, TypeB, Int] { 15 def apply(a:TypeB, b:TypeB): Int = error("TODO") //結(jié)果類型依賴Int和TypeB 16 } 17 implicitly[DepType[Int,TypeA,TypeB]] //> res1: Exercises.deptype.DepType[Int,Exercises.deptype.TypeA,Exercises.deptyp 18 //| e.TypeB] = Exercises.deptype$$anonfun$main$1$iab$2$@7722c3c3 19 implicitly[DepType[TypeB,TypeB,Int]] //> res2: Exercises.deptype.DepType[Exercises.deptype.TypeB,Exercises.deptype.Ty 20 //| peB,Int] = Exercises.deptype$$anonfun$main$1$bbi$2$@2ef3eef9 21 22 implicitly[DepType[TypeA,TypeB,TypeB]] //> res3: Exercises.deptype.DepType[Exercises.deptype.TypeA,Exercises.deptype.T 23 //| ypeB,Exercises.deptype.TypeB] = Exercises.deptype$$anonfun$main$1$abb$2$@24 24 //| 3c4f91 25 implicitly[DepType[TypeA,TypeA,TypeA]] //> res4: Exercises.deptype.DepType[Exercises.deptype.TypeA,Exercises.deptype.T 26 //| ypeA,Exercises.deptype.TypeA] = Exercises.deptype$$anonfun$main$1$aaa$2$@29 27 //| 1ae 28 //implicitly[DepType[TypeA,TypeA,TypeB]] //無法通過編譯 could not find implicit value for parameter e: Exercises.deptype.DepType[Exercises.deptype.TypeA,Exercises.deptype.TypeA,Exercises.deptype.TypeB] 29 30 def checkABC[A,B,C](a: A, b: B)(implicit instance: DepType[A,B,C]): C = error("TODO") 31 //> checkABC: [A, B, C](a: A, b: B)(implicit instance: Exercises.deptype.DepTyp 32 //| e[A,B,C])C 33 /* 34 val v_aaa: TypeA = checkABC(new TypeA{},new TypeA{}) 35 val v_iab: TypeB = checkABC(1,new TypeA{}) 36 val v_bbi: Int = checkABC(new TypeB{},new TypeB{}) 37 val v_aab: TypeB = checkABC(new TypeA{}, new TypeA{}) //ype mismatch; found : Exercises.deptype.TypeA required: Exercises.deptype.TypeB 38 */

以上例子利用依賴類型的類型關(guān)系實現(xiàn)了類型推導(dǎo)和驗證。

函數(shù)式編程重視概括抽象以方便函數(shù)組合從而實現(xiàn)高度的代碼重復(fù)使用。因為我們在進行函數(shù)式編程時最常遇到的類型款式是這樣的:F[A],所以我們在設(shè)計函數(shù)時會盡量對函數(shù)的參數(shù)進行針對F[A]的概括。但這樣也會對函數(shù)的使用者提出了苛刻要求:在調(diào)用函數(shù)時必須按照要求傳人F[A]類型的參數(shù),實際上又限制了函數(shù)的通用。Scalaz里的Unapply類型可以把許多不同款式的類型對應(yīng)成抽離的F[],A和TC。其中TC是個typeclass,用來引導(dǎo)編譯器進行類型推導(dǎo)。Unapply trait 如下:scalaz/Unapply.scala


trait Unapply[TC[_[_]], MA] {/** The type constructor */type M[_]/** The type that `M` was applied to */type A/** The instance of the type class */def TC: TC[M]/** Evidence that MA =:= M[A] */def leibniz: MA === M[A]/** Compatibility. */@inline final def apply(ma: MA): M[A] = leibniz(ma) }

從定義上分析:Unapply把MA拆分出M[]和A,但使用者必須提供TC - 一個施用在A的typeclass。

好了,我們先用一個簡單的例子來分析使用Unapply的背景和具體方式:


1 class TypeWithMap[F[_],A](fa: F[A])(implicit F: Functor[F]) { 2 def doMap[B](f: A => B) = F.map(fa)(f) 3 } 4 5 val mapList = new TypeWithMap(List(1,2,3)) //> mapList : Exercises.unapply.TypeWithMap[List,Int] = Exercises.unapply$$anon 6 //| fun$main$1$TypeWithMap$1@1d9b7cce 7 mapList.doMap {_ + 1} //> res2: List[Int] = List(2, 3, 4)
在這個例子里我們通過傳入一個F[A]類型來創(chuàng)建一個TypeWithMap類型實例, F是個Functor。如果我們傳入一個List, 因為List的類型款式是F[A]的,所以編譯器順利地把F[A]拆解成F[_]和A, 在例子里就是List和Int。那么如果我們試著傳入一個Function1[Int,Int]呢?


1 val mapFunc = new TypeWithMap( (_: Int) * 2 ) 2 //- not enough arguments for constructor TypeWithMap: (implicit F: scalaz.Functor[Any])Exercises.unapply.TypeWithMap[Any,A]. Unspecified value parameter F. 3 //- could not find implicit value for parameter F: scalaz.Functor[Any]
這個東西根本過不了編譯。主要是編譯器不曉得如何把Function1[A,A]對應(yīng)成F[A]。我們試試手工把類型款式對應(yīng)關(guān)系提供給編譯器:


1 val mapFunc2 = new TypeWithMap[({type l[x] = Function1[Int,x]})#l,Int]((_: Int) * 2) 2 //> mapFunc2 : Exercises.unapply.TypeWithMap[[x]Int => x,Int] = Exercises.unapp 3 //| ly$$anonfun$main$1$TypeWithMap$1@15ff3e9e 4 mapFunc2.doMap {_ + 1}(2) //> res3: Int = 5
看來沒問題,不過手工寫的還是有點復(fù)雜。Unapply是通過提供多種款式的類型隱式轉(zhuǎn)換實例(implicit instance)來進行類型匹配再分拆的。在上面的例子里Unapply提供了這么個款式的類型實例:


/**Unpack a value of type `M0[A0, B0]` into types `[b]M0[A0, b]` and `B`, given an instance of `TC` */implicit def unapplyMAB2[TC[_[_]], M0[_, _], A0, B0](implicit TC0: TC[({type λ[α] = M0[A0, α]})#λ]): Unapply[TC, M0[A0, B0]] {type M[X] = M0[A0, X]type A = B0} = new Unapply[TC, M0[A0, B0]] {type M[X] = M0[A0, X]type A = B0def TC = TC0def leibniz = refl}
這不就是我們例子的類型款式嘛。那我們看用Unapply能不能免去手工提供類型提示:


1 class TypeWithMap[F[_],A](fa: F[A])(implicit F: Functor[F]) {2 def doMap[B](f: A => B) = F.map(fa)(f)3 }4 object TypeWithMap {5 def apply[T](t: T)(implicit U: Unapply[Functor, T]) =6 new TypeWithMap[U.M,U.A](U.apply(t))(U.TC)7 }8 val umapList = TypeWithMap(List(1,2,3)) //> umapList : Exercises.unapply.TypeWithMap[[X]List[X],Int] = Exercises.unappl9 //| y$$anonfun$main$1$TypeWithMap$2@42e99e4a 10 umapList.doMap {_ + 1} //> res2: List[Int] = List(2, 3, 4) 11 val umapFunc = TypeWithMap((_: Int) * 2) //> umapFunc : Exercises.unapply.TypeWithMap[[X]Int => X,Int] = Exercises.unapp 12 //| ly$$anonfun$main$1$TypeWithMap$2@32eff876 13 umapFunc.doMap {_ + 1}(2) //> res3: Int = 5
看,不用我們提示編譯器,但我們必須提供TC的類型,在這里是Functor。注意:這里我們是對任意類型T進行分拆的。實際上U.apply(t)把T轉(zhuǎn)換成了U.M[U.A],看看Unapply的這段源代碼:


/** Evidence that MA =:= M[A] */def leibniz: MA === M[A]/** Compatibility. */@inline final def apply(ma: MA): M[A] = leibniz(ma)
從這里實現(xiàn)了MA >>> M[A]的轉(zhuǎn)換。
當(dāng)我看到用Unapply使Int這樣的簡單類型也能轉(zhuǎn)換成M[A]時覺得挺新鮮。看看traverse操作:


1 Applicative[Option].traverse(List(1,2,3))(a => (a + 1).some) 2 //> res6: Option[List[Int]] = Some(List(2, 3, 4))
traverse函數(shù)的款式是這樣的:


final def traverse[G[_], B](f: A => G[B])(implicit G: Applicative[G]): G[F[B]]
G是個Applicative,它的類型款式當(dāng)然是G[B]這樣的了,也就是我們必須提供f: A => G[B]這樣的函數(shù)款式。但如何解釋以下這句:


1 Monoid[Int].applicative.traverse(List(1,2,3))(a => a + 1) 2 //> res7: Int = 9
也就是說scalaz在什么地方把基本類型Int轉(zhuǎn)換成了G[B]這么個款式。從Unapply源代碼里查了一下,找到了這段:


sealed trait Unapply_4 {// /** Unpack a value of type `A0` into type `[a]A0`, given a instance of `TC` */implicit def unapplyA[TC[_[_]], A0](implicit TC0: TC[({type λ[α] = A0})#λ]): Unapply[TC, A0] {type M[X] = A0type A = A0} = new Unapply[TC, A0] {type M[X] = A0type A = A0def TC = TC0def leibniz = refl} }
這就解釋了上面的可能。當(dāng)然在Unapply.scala幾百行的源代碼中提供了大量不同類型款式的隱式轉(zhuǎn)換實例,大家可以在有需要的時候查找合適的分拆實例。下面我們再分析一個稍微復(fù)雜點的例子:假如我們想寫個針對List的sequence操作函數(shù):


1 def sequenceList[G[_], A](lga: List[G[A]])(implicit G: Applicative[G]): G[List[A]] =2 lga.foldRight(List[A]().point[G])((a,b) => G.apply2(a,b){_ :: _})3 //> sequenceList: [G#7905958[_#7912581], A#7905959](lga#7912582: List#3051[G#794 //| 05958[A#7905959]])(implicit G#7912583: scalaz#31.Applicative#28655[G#7905955 //| 8])G#7905958[List#3051[A#7905959]]6 val lli = List(List(1),List(2,3),List(4)) //> lli : List#8636[List#8636[Int#1125]] = List(List(1), List(2, 3), List(4))7 val los = List("a".some,"b".some,"c".some) //> los : List#8636[Option#1959[String#248]] = List(Some(a), Some(b), Some(c))8 //| 9 sequenceList(lli) //> res6: List#8636[List#3051[Int#1125]] = List(List(1, 2, 4), List(1, 3, 4)) 10 sequenceList(los) //> res7: Option#1959[List#3051[String#248]] = Some(List(a, b, c)) 11
?這個sequenceList函數(shù)對任何List[G[A]]這種傳入的類型款式都可以處理。但如果出現(xiàn)這樣的東西呢?


1 val lether = List(1.right[String],2.right[String],3.right[String]) 2 sequenceList(lether) //....required: List#3051[?G[?A]]
過不了編譯。看這個錯誤提示[?G[?A]],實際上編譯器期待的是個F[G[A]]款式的輸入?yún)?shù)但我們提供的是個F[G[A,B]]這么個款式,把編譯器搞糊涂了。我們試著給它點提示:


1 val lether = List(1.right[String],2.right[String],3.right[String]) 2 //> lether : List#8636[scalaz#31.\/#32660[String#17383,Int#1125]] = List(\/-(1 3 //| ), \/-(2), \/-(3)) 4 //sequenceList(lether) //....required: List#3051[?G[?A]] 5 sequenceList[({type l[x] = \/[String,x]})#l,Int](lether) 6 //> res8: scalaz#31.\/#32660[String#248,List#3051[Int#1125]] = \/-(List(1, 2, 3 7 //| ))

這樣就可以了。那么在Unapply里有沒有適合的款式呢?看看:


/**Unpack a value of type `M0[A0, B0]` into types `[a]M0[a, B0]` and `A`, given an instance of `TC` */implicit def unapplyMAB1[TC[_[_]], M0[_, _], A0, B0](implicit TC0: TC[({type λ[α] = M0[α, B0]})#λ]): Unapply[TC, M0[A0, B0]] {type M[X] = M0[X, B0]type A = A0} = new Unapply[TC, M0[A0, B0]] {type M[X] = M0[X, B0]type A = A0def TC = TC0def leibniz = refl}/**Unpack a value of type `M0[A0, B0]` into types `[b]M0[A0, b]` and `B`, given an instance of `TC` */implicit def unapplyMAB2[TC[_[_]], M0[_, _], A0, B0](implicit TC0: TC[({type λ[α] = M0[A0, α]})#λ]): Unapply[TC, M0[A0, B0]] {type M[X] = M0[A0, X]type A = B0} = new Unapply[TC, M0[A0, B0]] {type M[X] = M0[A0, X]type A = B0def TC = TC0def leibniz = refl}
?好像unapplMFAB1,unapplMFAB2這兩個實例都行。試試:


1 //val u1 = Unapply.unapplyMAB1[Applicative, \/, String, Int] //這個不行 2 //could not find implicit value for parameter TC0: scalaz#31.Applicative#28655[[α#75838]scalaz#31.\/#32660[α#75838,Int#1125]] 3 val u2 = Unapply.unapplyMAB2[Applicative, \/, String, Int] //這個可以 4 //> u2 : scalaz#31.Unapply#32894[scalaz#31.Applicative#28655,scalaz#31.\/#3266 5 //| 0[String#17383,Int#1125]]{type M#9842257[X#9842258] = scalaz#31.\/#32660[St 6 //| ring#17383,X#9842258]; type A#9842259 = Int#1125} = scalaz.Unapply_0$$anon$ 7 //| 13@47eaca72 8 sequenceList[u2.M,u2.A](lether) //> res9: Exercises#29.unapply#17810.u2#9836539.M#9842257[List#3051[Exercises#2 9 //| 9.unapply#17810.u2#9836539.A#9842259]] = \/-(List(1, 2, 3))
不過需要我們?nèi)斯づ卸莻€款式才合適。我們可以充分利用Unapply來編寫一個更概括的sequenceList函數(shù):


1 def sequenceListU[GA](lga: List[GA])(implicit U: Unapply[Applicative, GA]): U.M[List[U.A]] =2 sequenceList[U.M,U.A](U.leibniz.subst(lga))(U.TC)3 //> sequenceListU: [GA#10927512](lga#10936796: List#3051[GA#10927512])(implicit4 //| U#10936797: scalaz#31.Unapply#32894[scalaz#31.Applicative#28655,GA#10927515 //| 2])U#10936797.M#65840[List#3051[U#10936797.A#65842]]6 sequenceListU(lli) //> res10: List#8636[List#8636[Int#1125]] = List(List(1, 2, 4), List(1, 3, 4))7 sequenceListU(los) //> res11: Option#1959[List#8636[String#248]] = Some(List(a, b, c))8 sequenceListU(lether) //> res12: scalaz#31.\/#32660[String#248,List#8636[Int#1125]] = \/-(List(1, 2, 9 //| 3)) 10 sequenceListU(List(1,2,3)) //> res13: Int#1125 = 6
這個函數(shù)夠概括的了。主要是通過leibeniz.subst把List[GA]轉(zhuǎn)換成List[G[A]], 我們看看subst的源代碼:


sealed abstract class Leibniz[-L, +H >: L, A >: L <: H, B >: L <: H] {def apply(a: A): B = subst[Id](a)def subst[F[_ >: L <: H]](p: F[A]): F[B] ...


不要慌,注意下面這兩段代碼:


/** Evidence that MA =:= M[A] */def leibniz: MA === M[A]implicit def subst[A, B](a: A)(implicit f: A === B): B = f.subst[Id](a)
leibniz返回 MA === M[A], ?subst 傳入 A 返回 B。A >>>GA, B>>>G[A]。這樣上面例子中的U.leibniz.subst(lga)就把List[GA]轉(zhuǎn)換成了List[G[A]]。


總結(jié)

以上是生活随笔為你收集整理的Scalaz(27)- Inference Unapply :类型的推导和匹配的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。