[英]Cats-effect and the IO monad
我一直在嘗試掌握IO monad一段時間,這是有道理的。 如果我沒有弄錯的話,目標是分開副作用和實際執行的描述。 如下例所示,Scala有一種獲取環境變量的方法,該變量不是引用透明的。 出現了兩個問題。
問題1:這是一個參考透明的
問題2:如何正確(基於單元/屬性)測試這個? 不可能檢查是否相等,因為它將檢查內存引用,並且無法檢查內部函數,因為如果我沒有弄錯,則無法進行函數比較。 但是,我不想在單元測試中運行實際的副作用。 此外,這是設計錯誤還是濫用IO monad?
case class EnvironmentVariableNotFoundException(message: String) extends Exception(message)
object Env {
def get(envKey: String): IO[Try[String]] = IO.unit.flatMap((_) => IO.pure(tryGetEnv(envKey)))
private[this] def tryGetEnv(envKey: String): Try[String] =
Try(System.getenv(envKey))
.flatMap(
(x) =>
if (x == null) Failure(EnvironmentVariableNotFoundException(s"$envKey environment variable does not exist"))
else Success(x)
)
}
最好使用IO
來包含程序中來自不純源的值,就像示例中的System調用一樣。 這將返回一個IO[A]
,其內容為“ 我能夠通過不純的手段獲得A
”。 此后,您可以使用作用於A
純/引用透明函數,通過map
, flatMap
等。
這導致兩個答案。 我會問,你試圖測試的是什么屬性?
看一下這段代碼,我注意到tryGetEnv
的flatMap
是一個復雜到足以保證測試的東西。 您可以通過將此邏輯提取到純函數中來實現。 您可以(例如)重寫這個,這樣就有一個返回IO[String]
的函數,然后編寫一個(測試過的)函數,將其轉換為您想要的類型。
IO完全按照你的意思行事,但這顯然不包括使代碼引用透明! 如果你想在這里測試實際的副作用,你可以考慮將System作為參數傳遞並將其模擬為測試,就像在不使用IO
的程序中一樣。
總而言之,我考慮創建一個最小的函數來調用System
來創建一個IO[A]
(在這種情況下,一個IO[Try[String]]
)。 您可以選擇通過模擬來測試這個最小函數,但前提是您認為這樣做會增加價值。 在此之外,您可以編寫帶有A
函數,並通過將純值傳遞給這些函數來測試它們。 請記住, IO
上的map
簽名如下,這里的f
是純(可測試)函數!
sealed abstract class IO[+A] {
def map[B](f: A => B): IO[B]
^ f is a pure function!
test it by passing A values and verifying the Bs
盡管如此,這種模式鼓勵您僅在程序的最邊緣創建IO
值(例如,您的main
功能)。 然后,可以從純函數創建程序的其余部分,這些函數對程序運行時來自IO類型的值起作用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.