Scala doesn't have a ++
operator because it is not possible to implement one in it.
EDIT: As just pointed out in response to this answer, Scala 2.10.0 can implement an increment operator through use of macros. See this answer for details, and take everything below as being pre-Scala 2.10.0.
Let me elaborate on this, and I'll rely heavily on Java, since it actually suffers from the same problem, but it might be easier for people to understand it if I use a Java example.
To start, it is important to note that one of the goals of Scala is that the "built-in" classes must not have any capability that could not be duplicated by a library. And, of course, in Scala an Int
is a class, whereas in Java an int
is a primitive -- a type entirely distinct from a class.
So, for Scala to support i++
for i
of type Int
, I should be able to create my own class MyInt
also supporting the same method. This is one of the driving design goals of Scala.
Now, naturally, Java does not support symbols as method names, so let's just call it incr()
. Our intent then is to try to create a method incr()
such that y.incr()
works just like i++
.
Here's a first pass at it:
public class Incrementable {
private int n;
public Incrementable(int n) {
this.n = n;
}
public void incr() {
n++;
}
@Override
public String toString() {
return "Incrementable("+n+")";
}
}
We can test it with this:
public class DemoIncrementable {
static public void main(String[] args) {
Incrementable i = new Incrementable(0);
System.out.println(i);
i.incr();
System.out.println(i);
}
}
Everything seems to work, too:
Incrementable(0)
Incrementable(1)
And, now, I'll show what the problem is. Let's change our demo program, and make it compare Incrementable
to int
:
public class DemoIncrementable {
static public void main(String[] args) {
Incrementable i = new Incrementable(0);
Incrementable j = i;
int k = 0;
int l = 0;
System.out.println("i\t\tj\t\tk\tl");
System.out.println(i+"\t"+j+"\t"+k+"\t"+l);
i.incr();
k++;
System.out.println(i+"\t"+j+"\t"+k+"\t"+l);
}
}
As we can see in the output, Incrementable
and int
are behaving differently:
i j k l
Incrementable(0) Incrementable(0) 0 0
Incrementable(1) Incrementable(1) 1 0
The problem is that we implemented incr()
by mutating Incrementable
, which is not how primitives work. Incrementable
needs to be immutable, which means that incr()
must produce a new object. Let's do a naive change:
public Incrementable incr() {
return new Incrementable(n + 1);
}
However, this doesn't work:
i j k l
Incrementable(0) Incrementable(0) 0 0
Incrementable(0) Incrementable(0) 1 0
The problem is that, while, incr()
created a new object, that new object hasn't been assigned to i
. There's no existing mechanism in Java -- or Scala -- that would allow us to implement this method with the exact same semantics as ++
.
Now, that doesn't mean it would be impossible for Scala to make such a thing possible. If Scala supported parameter passing by reference (see "call by reference" in this wikipedia article), like C++ does, then we could implement it!
Here's a fictitious implementation, assuming the same by-reference notation as in C++.
implicit def toIncr(Int &n) = {
def ++ = { val tmp = n; n += 1; tmp }
def prefix_++ = { n += 1; n }
}
This would either require JVM support or some serious mechanics on the Scala compiler.
In fact, Scala does something similar to what would be needed that when it create closures -- and one of the consequences is that the original Int
becomes boxed, with possibly serious performance impact.
For example, consider this method:
def f(l: List[Int]): Int = {
var sum = 0
l foreach { n => sum += n }
sum
}
The code being passed to foreach
, { n => sum += n }
, is not part of this method. The method foreach
takes an object of the type Function1
whose apply
method implements that little code. That means { n => sum += n }
is not only on a different method, it is on a different class altogether! And yet, it can change the value of sum
just like a ++
operator would need to.
If we use javap
to look at it, we'll see this:
public int f(scala.collection.immutable.List);
Code:
0: new #7; //class scala/runtime/IntRef
3: dup
4: iconst_0
5: invokespecial #12; //Method scala/runtime/IntRef."<init>":(I)V
8: astore_2
9: aload_1
10: new #14; //class tst$$anonfun$f$1
13: dup
14: aload_0
15: aload_2
16: invokespecial #17; //Method tst$$anonfun$f$1."<init>":(Ltst;Lscala/runtime/IntRef;)V
19: invokeinterface #23, 2; //InterfaceMethod scala/collection/LinearSeqOptimized.foreach:(Lscala/Function1;)V
24: aload_2
25: getfield #27; //Field scala/runtime/IntRef.elem:I
28: ireturn
Note that instead of creating an int
local variable, it creates an IntRef
on the heap (at 0), which is boxing the int
. The real int
is inside IntRef.elem
, as we see on 25. Let's see this same thing implemented with a while loop to make clear the difference:
def f(l: List[Int]): Int = {
var sum = 0
var next = l
while (next.nonEmpty) {
sum += next.head
next = next.tail
}
sum
}
That becomes:
public int f(scala.collection.immutable.List);
Code:
0: iconst_0
1: istore_2
2: aload_1
3: astore_3
4: aload_3
5: invokeinterface #12, 1; //InterfaceMethod scala/collection/TraversableOnce.nonEmpty:()Z
10: ifeq 38
13: iload_2
14: aload_3
15: invokeinterface #18, 1; //InterfaceMethod scala/collection/IterableLike.head:()Ljava/lang/Object;
20: invokestatic #24; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
23: iadd
24: istore_2
25: aload_3
26: invokeinterface #29, 1; //InterfaceMethod scala/collection/TraversableLike.tail:()Ljava/lang/Object;
31: checkcast #31; //class scala/collection/immutable/List
34: astore_3
35: goto 4
38: iload_2
39: ireturn
No object creation above, no need to get something from the heap.
So, to conclude, Scala would need additional capabilities to support an increment operator that could be defined by the user, as it avoids giving its own built-in classes capabilities not available to external libraries. One such capability is passing parameters by-reference, but JVM does not provide support for it. Scala does something similar to call by-reference, and to do so it uses boxing, which would seriously impact performance (something that would most likely come up with an increment operator!). In the absence of JVM support, therefore, that isn't much likely.
As an additional note, Scala has a distinct functional slant, privileging immutability and referential transparency over mutability and side effects. The sole purpose of call by-reference is to cause side effects on the caller! While doing so can bring performance advantages in a number of situations, it goes very much against the grain of Scala, so I doubt call by-reference will ever be part of it.