Scala中的副作用

And*_*rea 15 monads functional-programming scala side-effects purely-functional

这些天我正在学习Scala.我对Haskell略有熟悉,虽然我不能声称对它很了解.

那些不熟悉Haskell的人的括号

我在Haskell中喜欢的一个特点是,不仅功能是一等公民,而且副作用(让我称之为行动)也是如此.执行时将赋予您类型值的操作a属于特定类型IO a.您可以像其他任何值一样传递这些操作,并以有趣的方式组合它们.

事实上,结合副作用是Haskell用它们做某事的唯一方法,因为你无法执行它们.相反,要执行的程序是由您的main函数返回的组合操作.这是一个巧妙的技巧,允许函数是纯粹的,同时让你的程序实际上做除了消耗功率之外的其他事情.

这种方法的主要优点是编译器知道您执行副作用的代码部分,因此它可以帮助您捕获它们的错误.

实际问题

在Scala中是否有某种方法可以让编译器类型为您检查副作用,以便 - 例如 - 您保证不会在某个函数中执行副作用?

Don*_*art 22

不,这在Scala中原则上是不可能的,因为该语言不强制引用透明性 - 语言语义忽略了副作用.您的编译器不会跟踪并强制执行副作用.

您将能够使用类型系统将某些操作标记IO类型,并且使用程序员规则,获得一些编译器支持,但没有编译器证明.


Ben*_*Ben 15

强制执行引用透明性的能力与Scala的目标是非常不兼容,即具有可与Java互操作的类/对象系统.

Java代码可能以任意方式不纯(并且在Scala编译器运行时可能无法进行分析)因此Scala编译器必须假设所有外部代码都是不纯的(为它们分配IO类型).要通过调用Java来实现纯Scala代码,您必须将调用包装为等效的unsafePerformIO.这增加了样板并使互操作性变得不那么令人愉快,但它变得更糟.

必须假设所有Java代码都在,IO除非程序员承诺否则将几乎杀死从Java类继承.必须假定所有继承的方法都在IO类型中; 这甚至适用于接口,因为Scala编译器必须假设在Java-land的某处存在不纯的实现.因此,您永远无法IO从Java类或接口派生任何非方法的Scala类.

更糟糕的是,即使对于在Scala中定义的类,理论上也可以在Java中使用不纯的方法定义未跟踪的子类,其实例可以作为父类的实例传递回Scala.因此,除非Scala编译器能够证明给定对象不可能是Java代码定义的类的实例,否则它必须假定对该对象的任何方法调用都可能调用由Java编译器编译的代码而不遵守什么函数返回结果不能IO做到.这将迫使几乎所有事情都进入IO.但是把所有东西都放进去就IO完全等同于什么都没有IO,只是没有跟踪副作用!

所以最终,Scala 鼓励你编写纯代码,但它并没有试图强制你这样做.就编译器而言,任何对任何事物的调用都可能产生副作用.