Indeed, this is pretty much the mathematical definition of factorial, but there
is a reason we don’t go with the simpler-looking one.
In the first version, by putting the multiplication in the parameter list for the
recursive method call, the multiplication
of the accumulator by n happens before
the recursive call to factorial. In the second version, the recursive call must be
evaluated first, and then the multiplication by n is carried out after that.
This puts the recursive call in the first
example into something that functional programmers call the “tail” position,
as in, it is the last thing that the function does.
Scala can then turn the recursive
function into a looped one automatically. Because the last thing the function
does is call itself, the whole thing can be
looped, and this means a lot less work
for the runtime. Recursion, while very
clever, carries some fairly heavy overhead. Stack frames must be created for
each call, and the stack can overflow if
the recursion is deep enough. Beyond
that, the JVM can do a lot to optimize
looped code (tricks such as inlining,
variable and register optimization, and
so on), but the JVM doesn’t always get a
chance to do this with recursive code.
In the meantime, many (if not most)
leading alternative languages for the JVM
include function literals and closures.
You might have noticed that I keep
referring to function literals as a concept
distinct from a closure. It is. A closure is
a kind of function literal that encloses
some values or variables from the surrounding scope.
When either of these is needed in
Java, inner or anonymous inner classes
provide the same features. These work,
but there is a lot of boilerplate code
involved, and it often ends up being
too much extra code to make the effort
worthwhile. For example, this is a simple
function literal in Scala:
scala> val primes = List( 2, 3, 5, 7, 11, 13)
primes: List[Int] = List( 2, 3, 5, 7, 11, 13)
scala> primes.map(n => n 3)
res0: List[Int] = List( 6, 9, 15, 21, 33, 39)
This would expand to enough code in
Java using an anonymous inner class that
most developers would just fall back on
a for loop to do the same operation. Of
course, then you have to create another
list to hold the results, and code to add
the results (or you change the values in
place in the list). All of these alternatives
have their own costs, either more code or
mutable state (which eventually might
lead to problems in concurrent systems).
The use of Java anonymous inner
classes to do what a function literal or
closure would do is significant, because
that’s how they are implemented in
Scala. This leads to quite an explosion
of classes being generated when you
compile a Scala file (each
closure gets its own class
generated). It is possible
that the method handles
being added into JDK 7
could help reduce the
number of extra class files
that are generated, and
it might speed up com-
pilation as well as reduce
the size of the compiled
binaries.
Comparing
CLR to
the JVM
It is interesting to compare Micro-
soft’s CLR to the JVM. From the
start, CLR was intended to be
source-language agnostic, and it
was launched with a variety of
supported source code languages, including C#, J# (a
Java-like language), and VB.NET. Since then, a number of
third-party source language options, such as Iron Python
and Iron Ruby, as well as new Microsoft-supported
languages, such as F#, have been added. The JVM, by
contrast, has always concentrated on supporting the Java
programming language first and foremost, but this has
not stopped other languages from targeting the JVM.
In fact, the JVM has many different source languages
that target its bytecode execution, drawn by the promise of easy cross-platform support and a runtime that is
installed on a large number of machines and devices. Robert
Tolksdorf and his group, is-research, have long provided an
exhaustive list of hundreds of languages targeting the JVM.
Although there are many languages running on the
JVM, not all of them do well with the limitations of the
JVM, which was not designed to support the wide diversity of language features that the union of all these
languages represents.
The most successful languages tend to work well with
the features that the JVM provides, and they find clever
ways to work around some of the limitations. Many of
these languages have started to move to the forefront of
the new generation of languages for the JVM.
These languages include Ruby (JRuby), Python
(Jython), Mirah (like Ruby with static typing), Clojure,
Groovy, Fantom, and others.
However, the language this article focuses on is
called Scala. It is the brainchild of Martin Odersky, who
has an intimate knowledge of the JVM and the Java
language. In fact, the javac compiler used for Java 1. 3
was based on Odersky’s GJ compiler. GJ was an experiment to extend the type system in Java with generics
(although Odersky is always quick to point out that
wildcards were not his idea).
COMMUNITY
JAVA IN ACTION
ABOUT US
Function Literals and Closures
Much has been said about the inclusion
of closures in the Java language, both
for and against. It seems fairly certain
that closures and function literals will be
included in Java 8, but that is still a year
or two away.
Traits
Java bucked the trend
toward multiple inheritance when it came out.
Multiple inheritance is
very powerful, but it brings
some issues along, such
as which method in which
superclass is actually being
referred to (the diamond
inheritance problem).
blog