[英]Can a function operating upon mutable data structure be referentially transparent?
我正在開發一個網絡應用程序並設計了以下特征來從遠程機器讀取文件:
trait ReadFileAlg[F[_], Dataset[_], Context]{
def read(path: String, ctx: Context): F[Dataset[String]]
}
final class NetworkContext{
private var fileDescriptor: Int = _
private var inputStream: InputStream = _
//etc
}
final class ReadFromRemoteHost[F[_]: Sync] extends ReadFileAlg[F, List, NetworkContext]{
override def read(path: String, ctx: NetworkContext): F[List[String]] = Sync[F].delay(
//read data from the remote host
)
}
我在這里看到的問題是該實現接受NetworkContext
作為可變參數,並包含與網絡連接相關的fileDescriptor
等字段。
這個函數read
引用透明嗎?
我認為是的,因為函數本身不提供對可變狀態的直接訪問(它在Sync[F].delay
),即使它接受可變數據結構作為參數。
IMO, read
的語義是
“當你申請我時,我是純潔的,但是當你運行我時,我有副作用。”
有人說這是一種花招:
...我們只是聲明一個返回 IO 類型的函數可能會產生任意效果,而沒有詳細說明這些效果是如何產生的。 該方案有兩個結果:首先,函數的類型告訴您它在運行時是引用透明的還是有副作用的。
例如,考慮以下具有可變狀態的對象
object Foo {
var x = 42
}
def f(foo: Foo.type): Int = foo.x
我們可以確認f
不是引用透明的,因為
assert(f(Foo) == 42) // OK
assert(f(Foo) == 42) // OK
...
Foo.x = -11
...
assert(f(Foo) == 42) // boom! Expression f(Foo) suddenly means something else
但是重新實現f
以“暫停”效果
def f(foo: Foo.type): IO[Int] = IO(foo.x)
這類似於
def f(foo: Foo.type): Unit => Int = _ => foo.x
然后
magicalAssert(f(Foo) == (_ => foo.x)) // OK
magicalAssert(f(Foo) == (_ => foo.x)) // OK
...
Foo.x = -11
...
magicalAssert(f(Foo) == (_ => foo.x)) // Still OK! Expression f(Foo) did not change meaning
這里的魔法斷言就像人腦一樣,不會遇到停機問題,因此能夠推導出函數行為的相等性,即,將f
求值應用於 value (_ => foo.x)
確實總是等於 value (_ => foo.x)
即使在某些時候Foo.x
被變異為-11
。
但是,運行f
的效果我們有
assert(f(Foo)() == 42) // OK
assert(f(Foo)() == 42) // OK
...
Foo.x = -11
...
assert(f(Foo)() == 42) // boom! expression f(Foo)() suddenly means something else
(注意我們是如何通過f(Foo)()
額外括號模擬IO.run
)
因此表達式f(Foo)
是引用透明的,但是表達式f(Foo)()
不是。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.