scala - Can a function operating upon mutable data structure be referentially transparent?
问题描述
I'm working on an network application and designed the following trait to read files from remote machines:
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
)
}
The problem I see here is that the implementation accepts NetworkContext
as a paramenter which is mutable and contains fields like fileDescriptor
which is related to a network connection.
Is this function read
referentially transparent?
I think yes, because the function itself does not provide direct access to a mutable state (it is under Sync[F].delay
) even though it accepts mutable data structure as an argument.
解决方案
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
评估应用于(_ => foo.x)
确实总是等于 value 的 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
(注意我们是如何IO.run
通过额外的括号来模拟的f(Foo)()
)
因此表达式f(Foo)
是引用透明的,但表达式f(Foo)()
不是。
推荐阅读
- c# - 存储在 Google Secret Manager 中的 DotNet 用户机密
- html - 控制台日志返回未定义
- android - 使用 Android 存储访问框架获取文件扩展
- php - 警告:session_start():找不到保存处理程序'/home1/proctekw/tmp;用于存储/检索数据的处理程序' - 会话启动失败
- django - Django从当前实例设置初始下拉值?
- arrays - Google电子表格,如何使用vlookup从其他表格中获取行?
- javascript - 为什么如果我不在当前函数中实例化我的变量,它会在下一个函数中被忽略?
- r - 修改和拆分 R 数据框中的行名
- reactjs - 我正在尝试使用 rowEvents 触发 react-bootstrap-table-2 中的操作,但 onClick 中的“this”未定义。有任何想法吗?
- javascript - 将 VueJS 连接到 RabbitMQ