2.7 zio入门——更多的Effect构造函数
2.7 更多的Effect構造函數
在本章的前面,我們了解了如何使用ZIO.effect構造函數將過程代碼轉換為ZIO效果。ZIO.effect構造函數是一種有用且通用的效果構造函數,但并不適合所有情況:
幸運的是,ZIO有許多強大的構造函數,可自定義失敗處理方案,異步代碼和其他常見數據類型。
2.7.1 純值與不純值
在介紹其他ZIO effect構造函數之前,我們首先需要討論參照透明性。如果我們始終可以在任何程序中將計算替換為其結果,同時仍保留其運行時行為,則諸如2 + 2之類的表達式將是透明的。
例如,考慮以下表達式:
我們可以將表達式2 + 2替換為其結果4,我們的程序的運行結果不會有任何改變。
相反,請考慮以下簡單程序,該程序從控制臺讀取一行輸入,然后將其輸出到控制臺:
我們無法將 echo 的代碼塊替換為其結果并維持程序的行為。 echo的結果值只是Unit值,因此,如果將echo替換為其返回值,則將具有:
val echo: Unit = ()
這兩個程序肯定不一樣。 第一個從用戶讀取輸入,并將輸入打印到控制臺,但是第二個程序什么也不做!
我們無法用其計算結果代替echo的原因是它會產生副作用。 它在一邊做事(從控制臺讀取和寫入控制臺)。 這與參照透明函數相反,后者沒有副作用,僅計算值。沒有副作用的表達式稱為純表達式,而主體為純表達式的函數稱為純函數。ZIO.effect構造函數采用副作用代碼,并將其轉換為純值,該值僅描述副作用。
為了了解這一點,讓我們重新了解回聲程序的ZIO實現:
import zio._val readLine = ZIO.effect(StdIn.readLine())def printLine(line: String) = ZIO.effect(println(line))val echo = for {line <- readLine_ <- printLine(line)} yield ()該程序是參照透明的,因為它僅建立了工作計劃,而沒有產生任何副作用。我們可以用生成的藍圖替換構建此藍圖的代碼,而我們仍然對此echo程序有一個計劃。
因此,我們可以將參照透明性作為另一種角度,去理解"函數式作用是工作藍圖"。函數式作用通過描述副作用而不是執行它們,從而使副作用代碼具有參照透明性。
描述和執行之間的這種分離,解耦"怎么做"和"做什么"之間的混亂,并賦予我們巨大的力量來變換和合成effect,正如我們在本書中將會看到的那樣。
參照透明性是將代碼轉換為ZIO時的一個重要概念,因為如果值或函數是參照透明的,則我們無需將其轉換為ZIO effect。但是,如果不純凈,則需要使用正確的effect構造函數將其轉換為ZIO效果。
即使您不小心將副作用代碼視為純代碼,ZIO也會嘗試做正確的事情。但是,將副作用代碼與ZIO代碼混合在一起可能會導致bug的產生,因此,最好謹慎使用正確的effect構造函數。作為附帶的好處,這將使您的代碼更易于同事閱讀和查看。
2.7.2用于純計算的effect構造器
These constructors are useful primarily when combining other ZIO effects, which have been constructed from side-effecting code, with pure code.
ZIO帶有各種effect構造器,可將純值轉換為ZIO effect。 這些構造函數在和從其他副作用代碼和純代碼構造的ZIO效果組合在一起時非常有用。
此外,即使是純代碼也可以從ZIO的某些功能中受益,例如環境,鍵入錯誤和堆棧安全性。
將純值轉換為ZIO效果的兩種最基本的方法是成功和失敗:
- ZIO.succeed構造函數將一個值轉換為以該值成功的effect。 例如,ZIO.succeed(42)構造一個以值42成功的effect。ZIO.succeed返回的效果的失敗類型為Nothing,因為使用此構造函數創建的效果不會失敗。
- ZIO.fail構造函數將值轉換為因該值而失敗的效果。 例如,ZIO.fail(new Exception)構造一個因指定的異常而失敗的效果。 ZIO.fail返回的效果的成功類型為Nothing,因為使用此構造函數創建的效果無法成功。
我們將看到無法成功的效果,無論是因為它們失敗還是因為它們永遠運行,通常都將Nothing用作成功類型。
除了這兩個基本構造函數外,還有許多其他構造函數可以將標準Scala數據類型轉換為ZIO效果。
這些構造函數將原始數據類型的成功和失敗案例轉換為ZIO成功和錯誤類型。
ZIO.fromEither構造函數將Either [E,A]轉換為IO [E,A]效果。如果Either是Left,則生成的ZIO效果將以E失敗,但是如果是Right,則生成的ZIO效果將以A成功。
ZIO.fromTry構造函數類似,不同之處在于錯誤類型固定為Throwable,因為Try只能通過Throwable失敗。
ZIO.fromOption構造函數更有趣,它說明了一個經常出現的想法。請注意,錯誤類型為None.type。這是因為Option只有一種錯誤模式。 Option [A]是帶有值的Some [A],或者是None,沒有其他信息。
因此,一個Option可能會失敗,但實際上只有一種可能會失敗的方法-值為None。該唯一失敗值的類型為None.type。
這些不是純值唯一的效果構造函數。在本章結尾的練習中,您將探索其他一些構造函數。
2.7.3用于副作用計算的 effect 構造器
最重要的效果構造函數是用于副作用計算的那些。 這些構造函數將過程代碼轉換為ZIO effect,因此它們成為將內容與方法分開的藍圖。
在本章的前面,我們介紹了ZIO.effect。 該構造函數捕獲有副作用的代碼,并將其計算推遲到以后,將代碼中引發的所有異常轉換為ZIO.fail值。
但是,有時我們想將副作用代碼轉換為ZIO效果,但是我們知道副作用代碼不會引發任何異常。 例如,檢查系統時間或生成隨機變量絕對是副作用,但是它們不能引發異常。
對于這些情況,我們可以使用構造函數ZIO.effectTotal,它將程序代碼轉換為不會失敗的ZIO效果:
2.7.3.1 異步回調轉換
JVM生態系統中的許多代碼都是非阻塞的。 非阻塞代碼不會同步計算并返回值。 相反,當您調用異步函數時,必須提供一個回調。然后,當該值可用時,將使用該值調用您的回調。 (有時這被隱藏在Future或其他異步數據類型的后面。)
例如,假設我們在以下代碼段中顯示了非阻塞查詢API:
如果我們給此函數一個我們感興趣的id,它將在后臺查找用戶,但立即返回。 然后,在檢索到用戶之后,它將調用我們傳遞給該方法的回調函數。
在類型簽名中使用Option表示沒有用戶使用我們請求的ID。
在以下代碼段中,我們調用getUserByIdAsync,并傳遞一個回調,該回調將在接收到用戶名時簡單地打印出該用戶名:
請注意,對getUserByIdAsync的調用幾乎會立即返回,即使在調用回調函數之前需要一段時間(甚至幾秒鐘或幾分鐘),并且實際上會將用戶名打印到控制臺上。
基于回調的API可以提高性能,因為我們可以編寫更高效的代碼而不會浪費線程。 但是直接使用基于回調的異步代碼可能會很痛苦,導致代碼高度嵌套,從而難以將成功和錯誤信息傳播到正確的位置,并且無法安全地處理資源。
幸運的是,就像之前的Scala的Future一樣,ZIO允許我們獲取異步代碼,并將其轉換為ZIO功能效果。
我們需要執行此轉換的構造函數是ZIO.effectAsync,其類型簽名顯示在以下片段中:
ZIO.effectAsync的類型簽名可能很難理解,因此讓我們來看一個示例。
要將getUserByIdAsync過程代碼轉換為ZIO,我們可以使用ZIO.effectAsync構造函數,如下所示:
effectAsync提供的回調,并期望返回ZIO效果,因此,如果用戶存在于數據庫中,則我們使用ZIO.succeed將用戶名轉換為ZIO效果,然后使用此成功效果調用回調。另一方面,如果用戶不存在,則使用ZIO.fail將None轉換為ZIO效果,然后使用此失敗的效果調用回調。
我們不得不花點時間將異步代碼轉換為ZIO函數,但是現在使用此查詢API時,我們不再需要處理回調。現在,我們可以像對待任何其他ZIO函數一樣對待getUserById,并使用flatMap之類的方法來組合其返回值,而這些函數都不會阻塞,并且具有ZIO為我們提供圍繞資源安全性的所有保證。
一旦getUserById計算的結果可用,我們將繼續創建的藍圖中的其他計算。
請注意,在effectAsync中,回調函數只能被調用一次,因此不適用于轉換所有異步API。如果回調可能被調用多次,則可以在ZStream上使用effectAsync構造函數,這將在本書后面討論。
我們將在本章中介紹的最后一個構造函數是ZIO.fromFuture,它將將創建Future的函數轉換為ZIO效果。
此構造函數的類型簽名如下:
因為Future是正在運行的計算,所以我們在執行此操作時必須非常小心。 fromFuture構造函數不需要Future。 相反,構造函數采用函數ExecutionContext => Future [A],該函數描述了如何在給定ExecutionContext的情況下制作Future。
盡管在將Future轉換為ZIO效果時不需要使用提供的ExecutionContext,但是如果您使用上下文,則ZIO可以管理Future在更高級別上的運行位置。
如果可能,我們要確保我們的make函數實現創建一個新的Future,而不是返回已經運行的Future。 以下代碼顯示了執行此操作的示例:
還有許多其他構造函數可以從其他數據類型(例如java.util.concurrent.Future)創建ZIO效果,并提供第三方包以提供從Monix,Cats Effect和其他數據類型的轉換。
總結
以上是生活随笔為你收集整理的2.7 zio入门——更多的Effect构造函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分享几个关于UG装配的小问题,干货满满!
- 下一篇: qt 绘制五角星图形