What is the motivation for assigning Scala for evaluating Unit rather than for assigned value?

What is the motivation for assigning Scala to calculate Unit, and not to the assigned value?

A common pattern in I / O programming is the following:

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

But this is not possible in Scala, because ...

 bytesRead = in.read(buffer) 

.. returns Unit, not the new bytesRead value.

An interesting thing seems to be to leave a functional language. I wonder why this was done?

+77
assignment-operator scala io functional-programming
Jan 04 '10 at
source share
8 answers

I advocated that assignments return the assigned value, not unit. Martin and I went back and forth, but his argument was that adding value to the stack just to pop up, 95% of the time was a waste of byte codes and adversely affected performance.

+77
Jan 04 '10 at 16:18
source share

I am not attached to inside information for actual reasons, but my suspicion is very simple. Scala makes side effects uncomfortable, which programmers naturally would prefer to understand.

He does this in many ways. For example, you do not have a for loop where you declare and modify a variable. You cannot (easily) mutate a state in a while at the same time as you check a condition, which means that you often have to repeat the mutation immediately before and at the end of it. Variables declared inside the while block are not visible from the while test condition, which makes do { ... } while (...) much less useful. And so on.

Workaround:

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

For whatever it costs.

As an alternative explanation, perhaps Martin Odersky had to deal with several very ugly mistakes arising from such use, and decided to ban it from his language.

EDIT

David Pollack responded with some factual evidence that is clearly supported by the fact that Martin Odersky himself commented on his answer, paying tribute to the performance-related arguments put forward by Pollack.

+17
Jan 04 '10 at
source share

This happened as part of Scala, which has a more "formally correct" type system. Formally speaking, assignment is a purely side effect and therefore should return Unit . This has some nice consequences; eg:

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

The state_= method returns Unit (as expected for the installer) precisely because the assignment returns Unit .

I agree that for C-style templates, such as copying a stream or similar, this particular design decision can be a little annoying. However, this is actually relatively unproblematic in general and does contribute to the overall consistency of the type system.

+9
Jan 04
source share

Perhaps this is due to the principle of the command line ?

CQS tends to be popular at the intersection of OO styles and functional programming, as it creates an obvious distinction between object methods that perform or do not have side effects (i.e. modify an object). Applying CQS to variable assignments takes it further than usual, but the same idea applies.

A brief illustration of why CQS is useful: consider a hypothetical hybrid F / OO language with the List class, which has the Sort , Append , First and Length methods. In the imperative style of OO, you can write this function:

 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() 

While in a more functional style, something like this will most likely be written:

 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() 

It seems that they are trying to do the same, but obviously one of the two is wrong and, not knowing more about the behavior of the methods, we cannot say which one.

However, using CQS, we will insist that if Append and Sort modify the list, they must return the unit type, which prevents us from creating errors using the second form, if we should not. Therefore, the presence of side effects also becomes implicit in the method signature.
+5
Jan 04
source share

I would suggest that this is necessary so that the program / language does not have side effects.

What you are describing is the deliberate use of a side effect, which is generally considered bad.

+4
Jan 04 '10 at
source share

This is not the best style for using assignment as a Boolean expression. You are doing two things at the same time, which often lead to errors. Using the Scalas constraint excludes the use of "=" instead of "==".

+4
Jan 04 '10 at 11:08
source share
By the way, I find that the initial trick is dumb, even in Java. Why is this not so similar?
 for(int bytesRead = in.read(buffer); bytesRead != -1; bytesRead = in.read(buffer)) { //do something } 

Of course, the assignment appears twice, but at least bytesRead is in the area to which it belongs, and I do not play with funny assignment tricks ...

+2
Jan 04
source share

You may have a workaround for this if you have a reference type for indirection. In a naive implementation, you can use the following for arbitrary types.

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

Then, with the constraint that you will need to use ref.value to access the link after that, you can write the while predicate as

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

and you can perform a check against bytesRead more implicit way without typing it.

0
Jun 18 '10 at 23:03
source share



All Articles