2.4 zio入门——ZIO类型参数
2.4 ZIO類型參數(shù)
我們之前說過,類型 ZIO [R,E,A]的值是一種 functional effect,需要環(huán)境R,并且可能因E失敗或成功返回A。
現(xiàn)在,我們也了解了ZIO effect 成為并發(fā)工作流程的藍圖意味著什么,以及如何組合效果,下面讓我們詳細討論每個ZIO類型參數(shù):
- R是 effect 要執(zhí)行效果所需的環(huán)境。一般指的是 effect 需要的一些依賴關(guān)系,例如訪問數(shù)據(jù)庫或日志記錄服務,或者效果可能不需要任何環(huán)境,在這種情況下,類型參數(shù)將為Any。
- E是效果可能失效的值的類型。這可能是Throwable或Exception,但也可能是特定領域的錯誤類型,或者效果可能根本無法失敗,在這種情況下,type參數(shù)將為Nothing。
- A是效果可以成功的值的類型。可以將其視為效果的返回值或輸出。
理解這些類型參數(shù)的一種有用方法是將ZIO effect 想象為函數(shù)R => Either [E,A]。這并不是ZIO的實現(xiàn)方式(此定義不允許我們編寫并發(fā),異步或資源安全的代碼
算子),但這是一個有用的思維模型。
以下代碼段定義了這種 ZIO effect 的 toy model:
final case class ZIO[-R, +E, +A](run: R => Either[E, A])從該定義可以看出,R參數(shù)是輸入(為了執(zhí)行效果,必須提供R類型的值),而E和A參數(shù)是輸出。 輸入被聲明為逆變的,而輸出被聲明為協(xié)變的。有關(guān) variance 的更詳細討論,請參見附錄。 否則,只要知道Scala variance注釋會改善類型推斷,這就是ZIO使用它們的原因。
讓我們看看如何使用這種思維模型來實現(xiàn)一些基本的構(gòu)造函數(shù)和運算符:
final case class ZIO[-R, +E, +A](run: R => Either[E, A]) {self =>def map[B](f: A => B): ZIO[R, E, B] = ZIO(r => self.run(r).map(f))def flatMap[R1 <: R, E1 >: E, B](f: A => ZIO[R1, E1, B]): ZIO[R1, E1, B] = ZIO(r => self.run(r).fold(ZIO.fail(_), f).run(r)) }object ZIO {def effect[A](a: => A): ZIO[Any, Throwable, A] = ZIO(_ =>try Right(a)catch {case t: Throwable => Left(t)})def fail[E](e: => E): ZIO[Any, E, Nothing] = ZIO(_ => Left(e)) }ZIO.effect方法將代碼塊包裝在一個effect中,將異常轉(zhuǎn)換為Left值,并將成功轉(zhuǎn)換為Right值。請注意,ZIO.effect的參數(shù)是按名稱命名的(使用 :=> 符號),這可以防止對代碼進行急切的計算,從而允許ZIO創(chuàng)建描述執(zhí)行的值。
我們還實現(xiàn)了前面討論的flatMap運算符,該運算符使我們可以順序組合效果。 flatMap的實現(xiàn)如下:
如上所述,ZIO effect 實際上并不是這樣實現(xiàn)的,但是執(zhí)行一個效果,獲取其結(jié)果然后將其傳遞給下一個效果的基本思想是一個準確的心理模型,它將幫助你在使用zio的過程中思考。
我們將很快學習更多的方法來處理成功的ZIO值,但是現(xiàn)在讓我們著重研究錯誤和環(huán)境類型,以建立其對他們的直覺,因為他們可能不那么直觀。
2.4.1 錯誤類型
錯誤類型表示效果潛在的失敗原因。 錯誤類型很有用,因為它允許我們使用類似 flatMap 這樣的運算符來處理成功的返回值,同時將錯誤處理推遲到更高級別。 這使我們能夠?qū)W⒂诔绦虻摹翱鞓分贰辈⒃谡_的地方處理錯誤。
例如,假設我們要編寫一個簡單的程序,該程序從用戶那里獲得兩個數(shù)字并將它們相乘:
請注意,readInt的返回類型為ZIO [Any,NumberFormatException,Int],指示它不需要任何環(huán)境,并且如果成功的話,可以返回整數(shù),如果失敗,則拋出NumberFormatException。
錯誤類型的第一個好處是,我們僅憑其簽名就知道該函數(shù)將如何失敗。我們對readInt的實現(xiàn)一無所知,但僅查看類型簽名,我們就知道它可能因NumberFormatException而失敗,也不會因任何其他錯誤而失敗。這非常強大,因為我們確切知道潛在的錯誤類型,而且我們永遠不必借助“防御性編程”來處理未知錯誤。第二個好處是,假設效果成功,我們就可以對效果結(jié)果進行操作,將錯誤處理推遲到以后。
如果兩個readInt調(diào)用均失敗,并出現(xiàn)NumberFormatException,則readAndSumTwoInts也將失敗,并出現(xiàn)異常,并中止求和。此簿記將自動為我們處理。我們可以直接將x和y相乘,而不必顯式地處理失敗的可能性。這將錯誤處理邏輯推遲給調(diào)用方,后者可以重試,報告或推遲更高級別的處理。
能夠了解效果可能如何失敗并將錯誤推遲到應用程序的更高級別很有用,但是在某些時候,我們需要能夠處理一些或所有錯誤。
要處理我們的ZIO玩具模型中的錯誤,我們實現(xiàn)一個名為foldM的運算符,如果原始效果失敗,它將使我們執(zhí)行一個效果,如果成功,則將執(zhí)行另一個操作:
final case class ZIO [-R, +E, +A](run : R => Either [E, A]) {self =>def foldM [R1 <: R, E1, B](failure : E => ZIO [R1, E1, B],success : A => ZIO [R1, E1, B]) : ZIO [R1, E1, B] = ZIO(r => self.run(r).fold(failure, success).run(r))}實際上,該實現(xiàn)與我們上面介紹的flatMap非常相似。 我們只是使用failure函數(shù)在出現(xiàn)錯誤的情況下返回新的效果,然后運行該效果。
錯誤類型最有用的功能之一是能夠使指定效果完全不會失敗,這可能是因為已經(jīng)捕獲并處理了它的錯誤。
在ZIO中,我們通過指定Nothing作為錯誤類型來做到這一點。 由于沒有Nothing類型的值,因此我們知道,如果我們有Either [Nothing,A],則它必須是Right。 我們可以使用它來實現(xiàn)錯誤處理運算符,這些運算符可以讓我們靜態(tài)地證明效果不會因為我們已經(jīng)處理了所有錯誤而失敗。
2.4.2 環(huán)境類型
現(xiàn)在,我們對錯誤類型有了一些印象,接下來讓我們關(guān)注環(huán)境類型。
我們可以通過Any來為任何不需要環(huán)境的effect進行建模。畢竟,如果effect需要類型為Any的值,則可以使用 ()(單位值),42或任何其他值來運行效果。 因此,可以使用任何類型的值運行的 effect 實際上是不需要任何特定類型環(huán)境的 effect。
使用環(huán)境的兩個基本操作是訪問環(huán)境(例如,獲得對數(shù)據(jù)庫的訪問以執(zhí)行某些操作)和提供環(huán)境(在測試的時候提供測試環(huán)境,在生產(chǎn)的時候提供生產(chǎn)環(huán)境)。
我們可以在ZIO的玩具模型中實現(xiàn)這一點,如以下代碼片段所示:
final case class ZIO[-R, +E, +A](run: R => Either[E, A]) {self =>def provide(r: R): ZIO[Any, E, A] = ZIO(_ => self.run(r))}object ZIO {def environment[R]: ZIO[R, Nothing, R] =ZIO(r => Right(r))}如您所見,provider運算符會返回不需要任何環(huán)境的新效果。 環(huán)境構(gòu)造函數(shù)使用所需的環(huán)境類型創(chuàng)建一個新效果,并將其作為成功值傳遞給環(huán)境。 這使我們可以訪問環(huán)境并使用其他運算符(例如map和flatMap)進行處理。
總結(jié)
以上是生活随笔為你收集整理的2.4 zio入门——ZIO类型参数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDUOJ 4513 吉哥系列故事——完
- 下一篇: 如何解决 win10 2016Excel