所以我正在閱讀“Scala with Cats”一書,我將在這里參考這句話:
請注意,Scala 的 Futures 并不是純函式式編程的一個很好的例子,因為它們不是參考透明的。
此外,還提供了一個示例,如下所示:
val future1 = {
// Initialize Random with a fixed seed:
val r = new Random(0L)
// nextInt has the side-effect of moving to
// the next random number in the sequence:
val x = Future(r.nextInt)
for {
a <- x
b <- x
} yield (a, b)
}
val future2 = {
val r = new Random(0L)
for {
a <- Future(r.nextInt)
b <- Future(r.nextInt)
} yield (a, b)
}
val result1 = Await.result(future1, 1.second)
// result1: (Int, Int) = (-1155484576, -1155484576)
val result2 = Await.result(future2, 1.second)
// result2: (Int, Int) = (-1155484576, -723955400)
我的意思是,我認為這是因為r.nextInt從不具有參考透明性的事實,對吧?因為identity(r.nextInt)永遠不會等于identity(r.nextInt),這是否意味著它identity也不是參照透明的?(或 Identity monad,以便與 Future 進行更好的比較)。如果計算的運算式是 RT,那么Future也將是 RT:
def foo(): Int = 42
val x = Future(foo())
Await.result(x, ...) == Await.result(Future(foo()), ...) // true
所以據我對這個例子的推理,幾乎每個函式和 Monad 型別都應該是非 RT 的。還是有什么特別之處Future?我也讀過了這個問題及其答案,但找不到我要找的東西。
uj5u.com熱心網友回復:
你實際上是對的,你正在觸及 FP 最挑剔的點之一;至少在Scala中。
從技術上講,Future它本身就是 RT。重要的是,與它不同的是,IO它不能將非 RT 內容包裝到 RT 描述中。但是,您可以對許多其他型別說同樣的話,例如List, 或Option; 那么為什么人們不對此大驚小怪呢?
好吧,就像許多事情一樣,魔鬼在細節中。
與Listor相反Option,Future通常用于非 RT 事物;例如 HTTP 請求或資料庫查詢。因此,人們強調Future在這些情況下不能保證 RT。更重要的是,在代碼庫中引入并發性
只有一個原因(不要與并行性混淆);否則,它將與 相同。因此,控制執行的時間和方式通常很重要。
這就是為什么貓建議在所有用例中使用FutureTryIOFuture
注意:您可以在此貓PR 及其鏈接討論中找到類似的討論: https ://github.com/typelevel/cats/pull/4182
uj5u.com熱心網友回復:
所以......參考透明度僅僅意味著您應該能夠用實際事物替換參考(反之亦然)而不改變整體的語意或行為。就像數學一樣。
所以,假設你有x = 4and y = 5,然后x y, 4 y, x 5, and4 5幾乎是一回事。并且可以隨時更換。
但是......只要看看以下兩件事......
val f1 = Future { println("Hi") }
val f2 = f1
val f1 = Future { println("Hi") }
val f2 = Future { println("Hi") }
您可以嘗試運行它。這兩個程式的行為不會相同。
ScalaFuture被熱切地評估......這意味著如果不將Future { println("Hi") }其作為單獨的行為執行,就無法實際撰寫代碼。
請記住,這不僅僅與擁有side effects. 是的,我在這里使用的示例println是 a side effect,但這只是為了使行為差異顯而易見。
即使你suspend在side effect里面使用了一些東西Future,你最終會得到兩個suspended side effects而不是一個。一旦將這些suspended side effects傳遞給interpreater,相同的動作將發生兩次。
在下面的示例中,即使我們print side-effect通過將其包裝在 IO 中來暫停它,即使在兩種情況下宇宙中的一切都完全相同,程式的擴展評估部分仍然會導致不同的行為。
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
// cpu bound
// takes around 80 miliseconds
// we have only 1 core
def veryExpensiveComputation(input: Int): Int = ???
def impl1(): Unit = {
val f1 = Future {
val result = veryExpensiveComputation(10)
IO {
println(result)
result
}
}
val f2 = f1
val f3 = f1
val futures = Future.sequence(Seq(f1, f2, f3))
val ios = Await.result(futures, 100 milli)
}
def impl2(): Unit = {
val f1 = Future {
val result = veryExpensiveComputation(10)
IO {
println(result)
result
}
}
val f2 = Future {
val result = veryExpensiveComputation(10)
IO {
println(result)
result
}
}
val f3 = Future {
val result = veryExpensiveComputation(10)
IO {
println(result)
result
}
}
val futures = Future.sequence(Seq(f1, f2, f3))
val ios = Await.result(futures, 100 milli)
}
第一個 impl 只會導致 1 次昂貴的計算,但第二個會觸發 3 次昂貴的計算。因此,在第二個示例中,程式將因超時而失敗。
IO如果使用或ZIO(不使用)正確撰寫Future,則在兩種實作中都會失敗并超時。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/515720.html
標籤:斯卡拉函数式编程单子
上一篇:在EPOCH中將日期轉換為時間戳
下一篇:如何實作以下順序?
