Scala赋值给Unit的动机是什么,而不是赋值?

Scala赋值给Unit的动机是什么,而不是赋值?

I / O编程的一个常见模式是做这样的事情:

while ((bytesRead = in.read(buffer)) != -1) { ... 

但是在Scala中这是不可能的,因为…

 bytesRead = in.read(buffer) 

..返回Unit,不是bytesRead的新值。

看起来像是一个有趣的事情,离开了function语言。 我想知道为什么这样做?

我主张让任务返回分配的价值而不是单位。 马丁和我之间来回转移,但是他的观点是,为了在95%的时间内突然出现这个价值,浪费了字节码,并对性能产生了负面影响。

我并不了解内幕消息的真实原因,但是我的怀疑很简单。 斯卡拉使副作用循环尴尬,以便程序员自然喜欢理解。

它在很多方面都是这样做的。 例如,你没有一个for循环声明和变异variables。 在testing条件的while你不能(容易)在while循环中改变状态,这意味着你经常必须在它之前和之后重复这个变异。 在while块中声明的variables在whiletesting条件下是不可见的,这使得do { ... } while (...)更加有用。 等等。

解决方法:

 while ({bytesRead = in.read(buffer); bytesRead != -1}) { ... 

无论什么是值得的。

作为另一种解释,也许马丁·奥德斯基(Martin Odersky)不得不面对一些由于这种使用而产生的非常丑陋的错误,并决定将其从语言中取缔。

编辑

大卫·波拉克已经回答了一些实际的事实, 奥德斯基本人评论他的回答,并明确支持波拉克提出的与performance有关的问题的论点。

这是Scala的一部分,它具有更“正式”的types系统。 从forms上讲,转让是一个纯粹的副作用,因此应该返回Unit 。 这确实有一些不错的结果。 例如:

 class MyBean { private var internalState: String = _ def state = internalState def state_=(state: String) = internalState = state } 

state_=方法返回Unit (就像setter所期望的那样),因为赋值返回Unit

我同意,对于复制stream或类似的C风格模式,这个特定的devise决定可能有点麻烦。 然而,它实际上是相对没有问题的,并且真正有助于types系统的整体一致性。

我想这是为了保持程序/语言免费的副作用。

你所描述的是故意使用一般情况下被认为是不好的副作用。

也许这是由于命令 – 查询分离原理?

CQS在OO和函数式编程风格的交集中趋于stream行,因为它在有或没有副作用(即改变对象)的对象方法之间产生明显的区别。 将CQS应用于variables赋值比平常更进一步,但同样的想法也适用。

一个为什么CQS是有用的简短说明:考虑一个假设的混合F / OO语言,其List类有SortAppendFirstLength 。 在命令式OO风格中,可能需要编写如下的函数:

 func foo(x): var list = new List(4, -2, 3, 1) list.Append(x) list.Sort() # list now holds a sorted, five-element list var smallest = list.First() return smallest + list.Length() 

而更多的function风格,更可能写这样的事情:

 func bar(x): var list = new List(4, -2, 3, 1) var smallest = list.Append(x).Sort().First() # list still holds an unsorted, four-element list return smallest + list.Length() 

这似乎是在试图做同样的事情,但显然其中一个是不正确的,不知道更多的方法的行为,我们不知道哪一个。

然而,使用CQS,我们坚持认为如果AppendSort改变列表,他们必须返回单元types,从而防止我们通过使用第二种forms创build错误。 因此副作用的存在也在方法签名中变得隐含。

使用赋值作为布尔expression式不是最好的风格。 你同时执行两件事情,经常导致错误。 用Scalas限制可以避免使用“=”而不是“==”。

顺便说一下:我发现了最初的愚蠢,即使在Java中。 为什么不像这样的事情?

 for(int bytesRead = in.read(buffer); bytesRead != -1; bytesRead = in.read(buffer)) { //do something } 

当然,这个任务会出现两次,但是至lessbytesRead属于它所属的范围,而且我不是在玩有趣的任务技巧。

只要你有间接引用types,你可以有一个解决方法。 在一个天真的实现中,可以使用以下任意types。

 case class Ref[T](var value: T) { def := (newval: => T)(pred: T => Boolean): Boolean = { this.value = newval pred(this.value) } } 

然后,在你必须使用ref.value来访问引用的约束下,你可以把你的while谓词写成

 val bytesRead = Ref(0) // maybe there is a way to get rid of this line while ((bytesRead := in.read(buffer)) (_ != -1)) { // ... println(bytesRead.value) } 

你可以用更隐含的方式来检查bytesRead而不必键入它。