CEO of Relevance
Stuart Halloway is the CEO of Relevance, Inc. (www.thinkrelevance.com). With co-founder Justin Gehtland, Stuart helps enterprises adopt emerging best practices such as Ruby on Rails. Justin and Stuart founded the Streamlined Framework (www.streamlinedframework.org), and authored Rails for Java Developers. Stuart is also the author of Component Development for the Java Platform. Prior to founding Relevance, Stuart was the Chief Architect at Near-Time, and the Chief Technical Officer at DevelopMentor.Presentations by Stuart Halloway
Prototype: Ajax and JavaScript
Prototype deserves its fame for Ajax support, and for easy integration with the Rails platform. In this presentation, you will learn to simplify Ajax development with Prototype as we work through a series of examples that demonstrate:* using Ajax.Request to manage XMLHTTPRequest
* automatically updating pages with Ajax.Updater
* polling with PeriodicalExecuter
* managing forms with Form.serialize
* responding to inputs with Event.observe
* simplifying DOM updates with Insertion
Ajax on Rails
Ruby on Rails is a great environment for building Ajax applications. Perhaps the best thing about the platform is the developers' unwavering commitment to being leaders in this field.Scriptaculous - The Ins and Outs
Scriptaculous is one of the most popular JavaScript effect and widget frameworks.Advanced Prototype: Ajax and JavaScript++
Building on the in-depth examination of the Prototype library from Prototype: Ajax and JavaScript ++, this session delves into the corners of Prototype that modify the DOM API and JavaScript's built-in types.JavaScript for Ajax Programmers
This presentation covers JavaScript from the perspective of an Ajax programmer. We assume that you may be using an Ajax toolkit, but still need to be able to read, modify, and test the JavaScript code in your application. You will learn the common idioms of JavaScript by looking at working code from the Ajax toolkits themselves.Relevance - Relevance Weblog
Thursday, August 14, 2008
This is Part Two of a series of articles on Java.next. In Part Two, I will look at how Java.next languages interoperate with Java.
Java interop is trivial in all of the Java.next languages. We have Java itself to thank for this--the Java Virtual Machine Specification makes it easy for other languages to reflect against and call Java code.
A Swing example
As a first example, consider calling into the Java Swing API to create an application [1] that has
- a frame
- button
- a button handler that responds with a model dialog
For starters, here is the application in plain old Java:
// Java
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Swing {
public static void main(String[] args) {
JFrame frame = new JFrame("Hello Swing");
JButton button = new JButton("Click Me");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
JOptionPane.showMessageDialog(null,
String.format("<html>Hello from <b>Java</b><br/>" +
"Button %s pressed", event.getActionCommand()));
}
});
frame.getContentPane().add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
Below, I will present the same Swing application, ported to the Java.next languages. Please take note of two things about these examples:
- For this post, I am presenting the languages in order of increasing syntactic distance from Java. This makes sense for porting a simple example from the well-known to the increasingly unfamiliar.
- The ports below are not best practice in the Java.next languages. They are deliberately simplistic, so that I can focus on Java interop. In later installments of this series I will show more idiomatic Java.next code.
Groovy Swing example
Groovy is the Java.next language that looks most like Java. Here is the same example in Groovy:
// Groovy
import javax.swing.JFrame
import javax.swing.JButton
import javax.swing.JOptionPane
import java.awt.event.ActionListener
frame = new JFrame("Hello Swing")
button = new JButton("Click Me")
button.addActionListener({
JOptionPane.showMessageDialog(null, """<html>Hello from <b>Groovy</b>
Button ${it.actionCommand} pressed""")
} as ActionListener)
frame.contentPane.add button
frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
frame.pack()
frame.visible = true
If you compare this to the Java example, it is almost the same code, minus a bunch of unnecessary ceremony. The Groovy version lets us omit:
- semicolons
- type declarations
- most parentheses
getandsetfor property access
The most important benefit, however, comes in the action listener. The Groovy version sports
- a multiline string (delimited by """)
- string interpolation of
it.actionCommand(inside ${}) - no need to write an anonymous inner class, simply pass an anonymous function
For a more idiomatic approach to Swing in Groovy, see the Groovy SwingBuilder project.
Since this post is about Java interop I will state the obvious: From Groovy, Java interop is entirely trivial.
Scala Swing example
Next, let's look at the Scala version:
// Scala (almost right, see below)
import javax.swing._
import java.awt.event.{ActionEvent, ActionListener}
object HelloWorld extends JFrame("Hello Swing") {
def showButtonMessage(msg: String) =
JOptionPane.showMessageDialog(null, String.format("""<html>Hello from <b>Scala</b>. Button %s pressed""", Array(msg)));
def main(args: Array[String]) {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
val button = new JButton("Click Me")
button.addActionListener((e:ActionEvent) => showButtonMessage(e.getActionCommand.toString))
getContentPane add button
pack
setVisible(true)
}
}
The Scala version offers many of the same advantages over Java that the Groovy version provided:
- fewer type declarations than Java
- fewer semicolons
- fewer parentheses
We also see a few items unique to Scala:
- In Scala, the import wildcard is
_, not*. In Scala,*is a valid identifier. (Scala's punctuation-friendly identifiers will be a big advantage later when I am writing DSLs.) - Scala has an inline syntax for importing multiple classes in a package.
- Since we only need one, we declare an
objectinstead of aclass. - Our object extends JFrame, and Scala lets us call the JFrame constructor inline, instead of having to declare a separate constructor.
Again, the most important differences are in the action listener. Like Groovy, Scala lets us skip the anonymous inner class ritual, and simply pass a function:
button.addActionListener((e:ActionEvent) =>
showButtonMessage(e.getActionCommand.toString))
That looks great, except I cheated a little. Scala's implementation of strong typing won't automatically coerce a function into an ActionListener, so the above code won't compile out of the box. Fortunately, Scala's implicit conversions let us have our cake and eat it too: strong typing plus much of the syntactic convenience of a looser type system. All we have to do is tell Scala the the conversion is legal:
// Yes, we can
implicit def actionPerformedWrapper(func: (ActionEvent) => Unit) =
new ActionListener { def actionPerformed(e:ActionEvent) = func(e) }
With this one-time setup in place, we can now pass a function where an ActionListener is expected.
There seem to be several projects to wrap Swing in more idiomatic Scala. Using one of these libraries you should be able to get a syntax cleaner than the sample code here. See ScalaGUI for one example.
From Scala, Java interop is trivial.
JRuby Swing example
Let's see how JRuby fares:
include Java
import javax.swing.JFrame
import javax.swing.JButton
import javax.swing.JOptionPane
import java.awt.event.ActionListener
button = JButton.new "Click Me"
button.add_action_listener do |evt|
JOptionPane.showMessageDialog(nil, <<-END)
<html>Hello from <b>JRuby</b>.
Button '#{evt.getActionCommand()}' clicked.
END
end
frame = JFrame.new "Hello Swing"
frame.content_pane.add button
frame.default_close_operation = JFrame::EXIT_ON_CLOSE
frame.pack
frame.visible = true
If you compare this to the earlier Groovy example, you will see almost exactly the same feature set:
- fewer type declarations
- fewer semicolons
- fewer parentheses
- simplified property access (no
getorset) - a multiline string (delimited by END)
- string interpolation of
evt.getActionCommand(the stuff inside #{})
The action listener callback is simplified in a fashion similar to the Groovy example. Ruby automatically generates the ActionListener from a block:
button.add_action_listener { |evt|
# do stuff
}
In the JRuby example I used Ruby conventions for method names, even on Java objects:
# Ruby
frame.content_pane
Java programmers expect camel case. As a convenience, JRuby supports both naming conventions:
# Groovy, Scala, or JRuby
frame.contentPane
Ruby's flexibility has encouraged a lot of experimentation with alternate syntaxes for Java interop. See JRUBY-903 for some of the history. For a more idiomatic approach to Swing in JRuby, see the Profligacy project.
From JRuby, Java interop is trivial.
Clojure Swing example
Here is the Clojure version:
; Clojure
; Clojure
(import '(javax.swing JFrame JButton JOptionPane))
(import '(java.awt.event ActionListener))
(let [frame (JFrame. "Hello Swing")
button (JButton. "Click Me")]
(.addActionListener button
(proxy [ActionListener] []
(actionPerformed [evt]
(JOptionPane/showMessageDialog nil,
(str "<html>Hello from <b>Clojure</b>. Button "
(.getActionCommand evt) " clicked.")))))
(.. frame getContentPane (add button))
(doto frame
(setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
pack
(setVisible true)))
Because Clojure is a Lisp, the syntax is radically different from the others. This deserves hours of discussion, or none. Since my focus here is on Java interop, I am going to save The Great Parenthesis Debate for a later entry in this series. For now, let us suspend judgment on syntax, and focus exclusively on the Java interop.
Importing Java classes is easy. import takes a list. The first element of the list is a package, and the remaining elements are classes to add to the current namespace. Note that this allows the import of multiple classes in a single line.
(import '(javax.swing JFrame JButton JOptionPane))
Creating a Java instance is easy. Use the (class. &args) form.
(JFrame. "Hello Swing")
There are multiple ways to call methods on a Java class. If you want to call a single method, you can use the (.methodName obj &args) form. For static calls, you can also use the (class/method &args) form:
(JOptionPane/showMessageDialog nil "A message")
Sometimes you want to chain multiple calls together. Where in Java you would say x.y().z(), in Clojure you can use the (.. x (y) (z)) form.
(.. frame (getContentPane) (add button))
The last three method calls in our example are all on the same frame object. With Clojure's doto form, you can perform multiple operations on an object without having to repeat the object each time.
(doto frame
(setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
pack
(setVisible true)))
As with the other examples, the action listener is the most interesting part. In Clojure, proxy will dynamically create a Java instance [2], allowing you to implement interfaces and methods as needed.
(proxy [ActionListener] []
(actionPerformed [evt] {do stuff here...}))
As with JRuby, this solution is more general, and requires more syntax, than the Groovy approach. Also as with JRuby, you can easily roll your own syntax.
From Clojure, Java interop is trivial.
Try this at home
The interop story in Java.next is almost boring: It Just Works. So to spice things up a little, here is an exercise in rolling your own constructs, inspired by the examples above. Consider Clojure's import, which can import multiple Java classes in a single line of code.
(import '(javax.swing JFrame JButton JOptionPane))
Why can't this be even more general? Try your hand at writing a custom import function in one of the Java.next languages. Some useful features might be
- import all the classes in a JAR
- import all the classes in the intersection of a package and a JAR
- import only interfaces
- import all classes macthing some criteria
- import all classes except those matching some criteria
Let me know what you come up with, and I will link to it here.
Conclusion
In the examples above, I have demonstrated how all of the Java.next libraries can trivially interoperate with Java. Each of examples called the Swing library with fewer lines of code than the Java version. More importantly, the Java.next versions capture the essence of the program with less ceremony.
Seamless interoperation with Java should not be the primary yardstick when measuring Java.next languages, because they all get it right. There are complexities and corner cases beyond what I have shown here, in all of the Java.next languages. But I consider the Java interop problem to be basically solved.
In these first two articles, I have stayed fairly close to Java style while demonstrating Java.next language features. With that groundwork in place, it is time to start using idiomatic Java.next. In the next installment of the Java.next series, we will look at how the Java.next languages support Domain-Specific Languages.
Notes
- This series is taken from the JVM Language Shootout talk. Check the schedule for a talk near you.
- Suggestions for improving the code samples above are most welcome.
- Thanks to Jason Rudolph, Glenn Vanderburg, and Greg Vaughn for reading an earlier draft of this article.
Footnotes
- I took the Swing application example from the JRuby samples, and ported it to the other Java.next languages.
- Clojure's
proxycreates classes as necessary behind the scenes. In Java.next, the dichotomy of class and object is not constantly center stage.
Revisions
- 2008/08/14. Updated Clojure example and prose per Rich Hickey's suggestion. Updated Groovy example to include pointer to SwingBuilder, per Andres Almiray. Updated JRuby example and prose based on suggestions from Nick Sieger and Ola Bini. Updated Scala example per Tony Morris's suggestion. Thanks for all the improvements!
Tuesday, August 12, 2008
This is Part One of a series of articles on Java.next. In Part One, I will explore the common ground shared by the Java.next languages.
I have chosen four languages which together represent "Java.next": Clojure, Groovy, JRuby, and Scala. At first glance, these languages are wildly different. Clojure is a Lisp. Groovy is the "almost Java" choice. JRuby has the beauty of Ruby, and the mindshare of Rails. Scala, unlike the others, brings the notion that we need more static typing.
As you might imagine, there is heated debate about which of these languages is best for some purpose, or best in general. Lost in the debate is the fact that these languages share a ton of common ground. They all evolved against a shared background, the Java language. Their design decisions are all influenced by what has worked well in Java, and what has failed.
In this article I will demonstrate two important points about the common ground these languages share:
- Over the last decade of coding in object-oriented, VM-based languages, we have learned a lot about writing expressive, maintainable applications. Java.next incorporates this knowledge, enabling essence over ceremony.
- The "essence vs. ceremony" design choices add up to a very different way of programming. The mental shift from Java to Java.next is a bigger shift than the previous shift from C/C++ to Java.
I have distilled the shared advantages of Java.next to eight points, which are explored in more detail below.
- everything is an object
- low-ceremony property definitions
- expressive collections
- functional programming
- overriding operators
- maintainable exception handling
- adding methods to existing objects
- roll-your-own constructs
Everything is an object
In Java, we live every day with the distinction between objects and primitives. This causes three practical problems:
- APIs must be duplicated: one method for objects, and another for primitives. Or worse, septlicated. One method for objects, and one each for different primitive types.
- The default (efficient, easy-to-use) numeric types have range limitations. Exceed them and your program breaks in mysterious ways.
- You cannot use intuitive math operators (+,-,etc.) with accurate numeric types.
In Java.next, everything is an object. You can invoke methods on all types using the same syntax.
; clojure
(. 1 floatValue)
1.0
// groovy
1.floatValue()
===> 1.0
# ruby
1.to_f
=> 1.0
// scala
1.floatValue
res1: Float = 1.0
Low-ceremony property definitions
In Java, to create a property, you must define a field, a getter, a setter, and (often) a constructor, all with appropriate protection modifiers. In Java.next, you can define all of these in a single step.
; clojure
(defstruct person :first-name :last-name)
// groovy
class Person {
def firstName
def lastName
}
# ruby
Person = Struct.new(:first_name, :last_name)
// scala
case class Person(firstName: String, lastName: String) {}
If you need to override (or omit) a getter, setter, or constructor for a class, you can also do that, without having to spell out all boilerplate versions of the other pieces.
And that's not all. All of these languages embrace TMTOWTDI (There's More Than One Way To Do It), so there are multiple variants on the approaches shown above.
Expressive collections
Java.next provides a convenient literal syntax for the most important collections: arrays and maps. In addition, you can string together multiple operations by passing function arguments, without having to write explicit iterators or loops. For example, to find the all the squares under 100 that are also odd:
; clojure
(filter (fn [x] (= 1 (rem x 2))) (map (fn [x] (* x x)) (range 10)))
(1 9 25 49 81)
// groovy
(1..10).collect{ it*it }.findAll { it%2 == 1}
===> [1, 9, 25, 49, 81]
# ruby
(1..10).collect{ |x| x*x }.select{ |x| x%2 == 1}
=> [1, 9, 25, 49, 81]
// scala
(1 to 10).map(x => x*x).filter(x => x%2 == 1)
res20: Seq.Projection[Int] = RangeMF(1, 9, 25, 49, 81)
There are similar conveniences for name/value collections, a.k.a. hashes or dictionaries.
Functional programming
The convenient collections described above are a special case of a more general idea: functional programming. Java.next supports functions as first class objects, allowing function arguments, functions that create new functions, and closures over the current scope. As a simple example, consider creating an adder function that adds some value chosen at runtime:
; clojure
(defn adder [x] (fn [y] (+ x y)))
// groovy
adder = { add -> { val -> val + add } }
# ruby
def adder(add)
lambda { |x| x + add }
end
// scala
def sum(a: Int)(b: Int) = a + b
Overriding operators
In Java, you cannot override operators. Math looks like this, for types like BigDecimal:
// Java math
balance.add(balance.multiply(interest));
Java.next allows you to override operators. This allows you to do create new types that feel like built-in types, e.g. you could write a ComplexNumber or RationalNumber that supports +, -, *, and /.
; Clojure
(+ balance (* balance interest))
// Groovy
balance + (balance * interest)
# JRuby
balance + (balance * interest)
// Scala (See [1])
balance + (balance * interest)
Maintainable exception handling
Checked exceptions are a failed experiment. Java code is bloated with checked exception handling code that tends to obscure intent without improving error handling. Worse yet, checked exceptions are a maintenance headache at abstraction boundaries. (New kinds of unrecoverable failures down the dependency chain should not necessitate recompilation!)
Java.next does not require you to declare checked exceptions, or to explicitly deal with checked exceptions from other code. It is a testimony to the power of Java (the platform) that other languages are free to ignore the ugliness of checked exceptions in Java (the language).
Adding methods to existing types
In Java, you cannot add methods to existing types. This leads to absurd object-mismodeling, as developers create utility classes that defy the point of OO:
// Java (from the Jakarta Commons)
public class StringUtils {
public static boolean isBlank(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
}
In Java.next, you can add methods to existing types:
; Clojure
(defmulti blank? class)
(defmethod blank? String [s] (every? #{\space} s))
(defmethod blank? nil [_] true)
// Groovy
String.metaClass.isBlank = {
length() == 0 || every { Character.isWhitespace(it.charAt(0)) }
}
# Ruby (from Rails)
class String
def blank?
empty? || strip.empty?
end
end
// Scala
class CharWrapper(ch: Char) {
def isWhitespace = Character.isWhitespace(ch)
}
implicit def charWrapper(ch: Character) = new CharWrapper(ch)
class BlankWrapper(s: String) {
def isBlank = s.isEmpty || s.forall(ch => ch.isWhitespace)
}
implicit def stringWrapper(s: String) = new BlankWrapper(s)
Roll-your-own constructs
In Java, you have the language and the libraries. The two are clearly distinct: you can write new libraries, but you cannot add language features.
In Java.next, the line between language and libraries is blurry. You can create new constructs that work like core language features. For example, Clojure provides an and function.
; clojure
(and 1 2) => 2
But maybe your problem domain isn't so binary. You need a most function, that returns true if most of its arguments evaluate to true. Clojure doesn't have this, but you can write one:
; clojure
(most 1 2) => true
(most 1 2 nil) => true
(most 1 nil nil) => false
The point here is not "Does my language need a 'most' conditional?" Probably not. The point is that different domains have different needs. In Java.next, the boundary between the language and the libraries is a minimized. You can adapt the language to your domain, instead of the other way around.
As another example, consider Ruby's attribute syntax:
# Ruby
class Account
attr_accessor :name
dsl_attribute :type
end
attr_accessor is built into the language. dsl_attribute is a library method that I wrote, which allows you to omit the "=" when assigning values, e.g.
# normal attributes
account.name = "foo"
# equals-free attributes
account.type checking
Conclusions
The Java.next languages share a ton of common ground. Although I've used small isolated examples for explanation, the real power comes from using these features together. Combining all the Java.next features leads to an entirely different style of coding.
- You do not have to code defensively, using a slew of factories, patterns, and dependency injection to keep your code testable and adaptable. Instead, you can build a minimal solution and evolve it.
- Instead of coding in your Java.next language, you can develop internal Domain-Specific Languages (DSLs) that better match your problem domain.
In my experience, this style of coding tends to reduce the size of a codebase by an order of magnitude, while improving readability.
Many people are looking for the "next big language." The next big language is already here, but it isn't a single language. It is the collection of ideas above (plus probably some I missed) as manifested in Java.next.
Does the transition to Java.next deserve the name "big"? Absolutely. In my experience, the move from Java to Java.next is every bit as big as the previous tectonic shifts in the industry, both in learning curve and in productivity advantages once you make the transition.
As an industry, we need to reset the bar to include Java.next. Once we have, we can have a conversation about the differences in these languages. I will take up the unique aspects of the Java.next languages in future installments of this series.
Notes
- This article is taken from the first half of the JVM Language Shootout talk that I wrote for NFJS. Check the schedule for a talk near you.
- Suggestions for improving the code samples above are most welcome.
- Thanks to Justin Gehtland, Jason Rudolph, Rob Sanheim, Glenn Vanderburg, and Greg Vaughn for reading an earlier draft of this article.
Footnotes
- The
BigDecimalexample does not work as I would expect on the Scala build I have (2.7.1.final). But the important point is that I could make it work by adding an implicit conversion. I am not dependent on the language designers, I can improve the language myself.
Revisions
- 2008/08/04: fixed errata, better Clojure example for expressive collections.
- 2008/08/12: added Rich Hickey's improved
blank?example for Clojure.
Tuesday, August 12, 2008
This is Part Two of a series of articles on Java.next. In Part Two, I will look at how Java.next languages interoperate with Java.
Java interop is trivial in all of the Java.next languages. We have Java itself to thank for this--the Java Virtual Machine Specification makes it easy for other languages to reflect against and call Java code.
A Swing example
As a first example, consider calling into the Java Swing API to create an application [1] that has
- a frame
- button
- a button handler that responds with a model dialog
For starters, here is the application in plain old Java:
// Java
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Swing {
public static void main(String[] args) {
JFrame frame = new JFrame("Hello Swing");
JButton button = new JButton("Click Me");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
JOptionPane.showMessageDialog(null,
String.format("<html>Hello from <b>Java</b><br/>" +
"Button %s pressed", event.getActionCommand()));
}
});
frame.getContentPane().add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
Below, I will present the same Swing application, ported to the Java.next languages. Please take note of two things about these examples:
- For this post, I am presenting the languages in order of increasing syntactic distance from Java. This makes sense for porting a simple example from the well-known to the increasingly unfamiliar.
- The ports below are not best practice in the Java.next languages. They are deliberately simplistic, so that I can focus on Java interop. In later installments of this series I will show more idiomatic Java.next code.
Groovy Swing example
Groovy is the Java.next language that looks most like Java. Here is the same example in Groovy:
// Groovy
import javax.swing.JFrame
import javax.swing.JButton
import javax.swing.JOptionPane
import java.awt.event.ActionListener
frame = new JFrame("Hello Swing")
button = new JButton("Click Me")
button.addActionListener({
JOptionPane.showMessageDialog(null, """<html>Hello from <b>Groovy</b>
Button ${it.actionCommand} pressed""")
} as ActionListener)
frame.contentPane.add button
frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
frame.pack()
frame.visible = true
If you compare this to the Java example, it is almost the same code, minus a bunch of unnecessary ceremony. The Groovy version lets us omit:
- semicolons
- type declarations
- most parentheses
getandsetfor property access
The most important benefit, however, comes in the action listener. The Groovy version sports
- a multiline string (delimited by """)
- string interpolation of
it.actionCommand(inside ${}) - no need to write an anonymous inner class, simply pass an anonymous function
Since this post is about Java interop I will state the obvious: From Groovy, Java interop is entirely trivial.
Scala Swing example
Next, let's look at the Scala version:
// Scala (almost right, see below)
import javax.swing._;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
object HelloWorld extends JFrame("Hello Swing") {
def showButtonMessage(msg: String) =
JOptionPane.showMessageDialog(null, String.format("""<html>Hello from <b>Scala</b>.
Button %s pressed""", Array(msg)));
def main(args: Array[String]) =
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
val button = new JButton("Click Me")
button.addActionListener((e:ActionEvent) =>
showButtonMessage(e.getActionCommand.toString))
getContentPane.add(button)
pack;
setVisible(true);
}
}
The Scala version offers many of the same advantages over Java that the Groovy version provided:
- fewer type declarations than Java
- fewer semicolons
- fewer parentheses
We also see a few items unique to Scala:
- In Scala, the import wildcard is
_, not*. In Scala,*is a valid identifier. (Scala's punctuation-friendly identifiers will be a big advantage later when I am writing DSLs.) - Since we only need one, we declare an
objectinstead of aclass. - Our object extends JFrame, and Scala lets us call the JFrame constructor inline, instead of having to declare a separate constructor.
Again, the most important differences are in the action listener. Like Groovy, Scala lets us skip the anonymous inner class ritual, and simply pass a function:
button.addActionListener((e:ActionEvent) =>
showButtonMessage(e.getActionCommand.toString))
That looks great, except I cheated a little. Scala's implementation of strong typing won't automatically coerce a function into an ActionListener, so the above code won't compile out of the box. Fortunately, Scala's implicit conversions let us have our cake and eat it too: strong typing plus much of the syntactic convenience of a looser type system. All we have to do is tell Scala the the conversion is legal:
// Yes, we can
implicit def actionPerformedWrapper(func: (ActionEvent) => Unit) =
new ActionListener { def actionPerformed(e:ActionEvent) = func(e) }
With this one-time setup in place, we can now pass a function where an ActionListener is expected.
There seem to be several projects to wrap Swing in more idiomatic Scala. Using one of these libraries you should be able to get a syntax cleaner than the sample code here. See ScalaGUI for one example.
From Scala, Java interop is trivial.
JRuby Swing example
Let's see how JRuby fares:
include Java
import javax.swing.JFrame
import javax.swing.JButton
import javax.swing.JOptionPane
import java.awt.event.ActionListener
button = JButton.new "Click Me"
button.add_action_listener(ActionListener.impl { |m,evt|
JOptionPane.showMessageDialog(nil, <<-END)
<html>Hello from <b>JRuby</b>.
Button '#{evt.getActionCommand()}' clicked.
END
})
frame = JFrame.new "Hello Swing"
frame.content_pane.add button
frame.default_close_operation = JFrame::EXIT_ON_CLOSE
frame.pack
frame.visible = true
If you compare this to the earlier Groovy example, you will see almost exactly the same feature set:
- fewer type declarations
- fewer semicolons
- fewer parentheses
- simplified property access (no
getorset) - a multiline string (delimited by END)
- string interpolation of
evt.getActionCommand(the stuff inside #{})
The action listener callback is simplified, but in a slightly different way. For implementing a Java interface, JRuby provides the impl method, which implements an interface and passes all methods to a block:
button.add_action_listener(ActionListener.impl { |method,evt|
# do stuff
END
This is more verbose than the Groovy technique, because it is more general: the unused method parameter allows for interfaces with more than one method.
Does this mean I am stuck with a verbose, general approach that is overkill for my needs? Absolutely not. In Java.next, you bend languages to your will (not the other way around). So, if you want JRuby to support a more Groovy-like syntax, roll it yourself! (See the Profligacy project for an example.) Ruby's flexibility has encouraged a lot of experimentation with alternate syntaxes for Java interop. See JRUBY-903 for some of the history.
In the JRuby example I used Ruby conventions for method names, even on Java objects:
# Ruby
frame.content_pane
Java programmers expect camel case. As a convenience, JRuby supports both naming conventions:
# Groovy, Scala, or JRuby
frame.contentPane
From JRuby, Java interop is trivial.
Clojure Swing example
Here is the Clojure version:
; Clojure
(import '(javax.swing JFrame JButton JOptionPane))
(import '(java.awt.event ActionListener))
(let [frame (new JFrame "Hello Swing")
button (new JButton "Click Me")]
(.addActionListener
button
(proxy [ActionListener] []
(actionPerformed
[evt]
(.showMessageDialog
JOptionPane
nil,
(str "<html>Hello from <b>Clojure</b>.<br/> Button " (.getActionCommand evt) " clicked.")))))
(.. frame (getContentPane) (add button))
(doto frame
(setDefaultCloseOperation (. JFrame EXIT_ON_CLOSE))
(pack)
(setVisible true)))
Because Clojure is a Lisp, the syntax is radically different from the others. This deserves hours of discussion, or none. Since my focus here is on Java interop, I am going to save The Great Parenthesis Debate for a later entry in this series. For now, let us suspend judgment on syntax, and focus exclusively on the Java interop.
Importing Java classes is easy. import takes a list. The first element of the list is a package, and the remaining elements are classes to add to the current namespace. Note that this allows the import of multiple classes in a single line.
(import '(javax.swing JFrame JButton JOptionPane))
Creating a Java instance is easy. Use the (new class &args) form.
(new JFrame "Hello Swing")
There are multiple ways to call methods on a Java class. If you want to call a single method, you can use the (.methodName obj &args) form. obj can be a class or an instance.
(.showMessageDialog JOptionPane nil "A message")
Sometimes you want to chain multiple calls together. Where in Java you would say x.y().z(), in Clojure you can use the (.. x (y) (z)) form.
(.. frame (getContentPane) (add button))
The last three method calls in our example are all on the same frame object. With Clojure's doto form, you can perform multiple operations on an object without having to repeat the object each time.
(doto frame
(setDefaultCloseOperation (. JFrame EXIT_ON_CLOSE))
(pack)
(setVisible true)))
As with the other examples, the action listener is the most interesting part. In Clojure, proxy will dynamically create a Java instance [2], allowing you to implement interfaces and methods as needed.
(proxy [ActionListener] []
(actionPerformed [evt] {do stuff here...}))
As with JRuby, this solution is more general, and requires more syntax, than the Groovy approach. Also as with JRuby, you can easily roll your own syntax.
From Clojure, Java interop is trivial.
Try this at home
The interop story in Java.next is almost boring: It Just Works. So to spice things up a little, here is an exercise in rolling your own constructs, inspired by the examples above. Consider Clojure's import, which can import multiple Java classes in a single line of code.
(import '(javax.swing JFrame JButton JOptionPane))
Why can't this be even more general? Try your hand at writing a custom import function in one of the Java.next languages. Some useful features might be
- import all the classes in a JAR
- import all the classes in the intersection of a package and a JAR
- import only interfaces
- import all classes macthing some criteria
- import all classes except those matching some criteria
Let me know what you come up with, and I will link to it here.
Conclusion
In the examples above, I have demonstrated how all of the Java.next libraries can trivially interoperate with Java. Each of examples called the Swing library with fewer lines of code than the Java version. More importantly, the Java.next versions capture the essence of the program with less ceremony.
Seamless interoperation with Java should not be the primary yardstick when measuring Java.next languages, because they all get it right. There are complexities and corner cases beyond what I have shown here, in all of the Java.next languages. But I consider the Java interop problem to be basically solved.
In these first two articles, I have stayed fairly close to Java style while demonstrating Java.next language features. With that groundwork in place, it is time to start using idiomatic Java.next. In the next installment of the Java.next series, we will look at how the Java.next languages support Domain-Specific Languages.
Notes
- This series is taken from the JVM Language Shootout talk. Check the schedule for a talk near you.
- Suggestions for improving the code samples above are most welcome.
- Thanks to Jason Rudolph, Glenn Vanderburg, and Greg Vaughn for reading an earlier draft of this article.
Footnotes
- I took the Swing application example from the JRuby samples, and ported it to the other Java.next languages.
- Clojure's
proxycreates classes as necessary behind the scenes. In Java.next, the dichotomy of class and object is not constantly center stage.
Tuesday, August 5, 2008
This is Part One of a series of articles on Java.next. In Part One, I will explore the common ground shared by the Java.next languages.
I have chosen four languages which together represent "Java.next": Clojure, Groovy, JRuby, and Scala. At first glance, these languages are wildly different. Clojure is a Lisp. Groovy is the "almost Java" choice. JRuby has the beauty of Ruby, and the mindshare of Rails. Scala, unlike the others, brings the notion that we need more static typing.
As you might imagine, there is heated debate about which of these languages is best for some purpose, or best in general. Lost in the debate is the fact that these languages share a ton of common ground. They all evolved against a shared background, the Java language. Their design decisions are all influenced by what has worked well in Java, and what has failed.
In this article I will demonstrate two important points about the common ground these languages share:
- Over the last decade of coding in object-oriented, VM-based languages, we have learned a lot about writing expressive, maintainable applications. Java.next incorporates this knowledge, enabling essence over ceremony.
- The "essence vs. ceremony" design choices add up to a very different way of programming. The mental shift from Java to Java.next is a bigger shift than the previous shift from C/C++ to Java.
I have distilled the shared advantages of Java.next to eight points, which are explored in more detail below.
- everything is an object
- low-ceremony property definitions
- expressive collections
- functional programming
- overriding operators
- maintainable exception handling
- adding methods to existing objects
- roll-your-own constructs
Everything is an object
In Java, we live every day with the distinction between objects and primitives. This causes three practical problems:
- APIs must be duplicated: one method for objects, and another for primitives. Or worse, septlicated. One method for objects, and one each for different primitive types.
- The default (efficient, easy-to-use) numeric types have range limitations. Exceed them and your program breaks in mysterious ways.
- You cannot use intuitive math operators (+,-,etc.) with accurate numeric types.
In Java.next, everything is an object. You can invoke methods on all types using the same syntax.
; clojure
(. 1 floatValue)
1.0
// groovy
1.floatValue()
===> 1.0
# ruby
1.to_f
=> 1.0
// scala
1.floatValue
res1: Float = 1.0
Low-ceremony property definitions
In Java, to create a property, you must define a field, a getter, a setter, and (often) a constructor, all with appropriate protection modifiers. In Java.next, you can define all of these in a single step.
; clojure
(defstruct person :first-name :last-name)
// groovy
class Person {
def firstName
def lastName
}
# ruby
Person = Struct.new(:first_name, :last_name)
// scala
case class Person(firstName: String, lastName: String) {}
If you need to override (or omit) a getter, setter, or constructor for a class, you can also do that, without having to spell out all boilerplate versions of the other pieces.
And that's not all. All of these languages embrace TMTOWTDI (There's More Than One Way To Do It), so there are multiple variants on the approaches shown above.
Expressive collections
Java.next provides a convenient literal syntax for the most important collections: arrays and maps. In addition, you can string together multiple operations by passing function arguments, without having to write explicit iterators or loops. For example, to find the all the squares under 100 that are also odd:
; clojure
(filter (fn [x] (= 1 (rem x 2))) (map (fn [x] (* x x)) (range 10)))
(1 9 25 49 81)
// groovy
(1..10).collect{ it*it }.findAll { it%2 == 1}
===> [1, 9, 25, 49, 81]
# ruby
(1..10).collect{ |x| x*x }.select{ |x| x%2 == 1}
=> [1, 9, 25, 49, 81]
// scala
(1 to 10).map(x => x*x).filter(x => x%2 == 1)
res20: Seq.Projection[Int] = RangeMF(1, 9, 25, 49, 81)
There are similar conveniences for name/value collections, a.k.a. hashes or dictionaries.
Functional programming
The convenient collections described above are a special case of a more general idea: functional programming. Java.next supports functions as first class objects, allowing function arguments, functions that create new functions, and closures over the current scope. As a simple example, consider creating an adder function that adds some value chosen at runtime:
; clojure
(defn adder [x] (fn [y] (+ x y)))
// groovy
adder = { add -> { val -> val + add } }
# ruby
def adder(add)
lambda { |x| x + add }
end
// scala
def sum(a: Int)(b: Int) = a + b
Overriding operators
In Java, you cannot override operators. Math looks like this, for types like BigDecimal:
// Java math
balance.add(balance.multiply(interest));
Java.next allows you to override operators. This allows you to do create new types that feel like built-in types, e.g. you could write a ComplexNumber or RationalNumber that supports +, -, *, and /.
; Clojure
(+ balance (* balance interest))
// Groovy
balance + (balance * interest)
# JRuby
balance + (balance * interest)
// Scala (See [1])
balance + (balance * interest)
Maintainable exception handling
Checked exceptions are a failed experiment. Java code is bloated with checked exception handling code that tends to obscure intent without improving error handling. Worse yet, checked exceptions are a maintenance headache at abstraction boundaries. (New kinds of unrecoverable failures down the dependency chain should not necessitate recompilation!)
Java.next does not require you to declare checked exceptions, or to explicitly deal with checked exceptions from other code. It is a testimony to the power of Java (the platform) that other languages are free to ignore the ugliness of checked exceptions in Java (the language).
Adding methods to existing types
In Java, you cannot add methods to existing types. This leads to absurd object-mismodeling, as developers create utility classes that defy the point of OO:
// Java (from the Jakarta Commons)
public class StringUtils {
public static boolean isBlank(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
}
In Java.next, you can add methods to existing types:
; Clojure
(defn blank? [x]
(cond (string? x) (= (.length x) 0)
(nil? x) true
(throw (new Exception "time to support some new types"))))
// Groovy
String.metaClass.isBlank = {
length() == 0 || every { Character.isWhitespace(it.charAt(0)) }
}
# Ruby (from Rails)
class String
def blank?
empty? || strip.empty?
end
end
// Scala
class CharWrapper(ch: Char) {
def isWhitespace = Character.isWhitespace(ch)
}
implicit def charWrapper(ch: Character) = new CharWrapper(ch)
class BlankWrapper(s: String) {
def isBlank = s.isEmpty || s.forall(ch => ch.isWhitespace)
}
implicit def stringWrapper(s: String) = new BlankWrapper(s)
Roll-your-own constructs
In Java, you have the language and the libraries. The two are clearly distinct: you can write new libraries, but you cannot add language features.
In Java.next, the line between language and libraries is blurry. You can create new constructs that work like core language features. For example, Clojure provides an and function.
; clojure
(and 1 2) => 2
But maybe your problem domain isn't so binary. You need a most function, that returns true if most of its arguments evaluate to true. Clojure doesn't have this, but you can write one:
; clojure
(most 1 2) => true
(most 1 2 nil) => true
(most 1 nil nil) => false
The point here is not "Does my language need a 'most' conditional?" Probably not. The point is that different domains have different needs. In Java.next, the boundary between the language and the libraries is a minimized. You can adapt the language to your domain, instead of the other way around.
As another example, consider Ruby's attribute syntax:
# Ruby
class Account
attr_accessor :name
dsl_attribute :type
end
attr_accessor is built into the language. dsl_attribute is a library method that I wrote, which allows you to omit the "=" when assigning values, e.g.
# normal attributes
account.name = "foo"
# equals-free attributes
account.type checking
Conclusions
The Java.next languages share a ton of common ground. Although I've used small isolated examples for explanation, the real power comes from using these features together. Combining all the Java.next features leads to an entirely different style of coding.
- You do not have to code defensively, using a slew of factories, patterns, and dependency injection to keep your code testable and adaptable. Instead, you can build a minimal solution and evolve it.
- Instead of coding in your Java.next language, you can develop internal Domain-Specific Languages (DSLs) that better match your problem domain.
In my experience, this style of coding tends to reduce the size of a codebase by an order of magnitude, while improving readability.
Many people are looking for the "next big language." The next big language is already here, but it isn't a single language. It is the collection of ideas above (plus probably some I missed) as manifested in Java.next.
Does the transition to Java.next deserve the name "big"? Absolutely. In my experience, the move from Java to Java.next is every bit as big as the previous tectonic shifts in the industry, both in learning curve and in productivity advantages once you make the transition.
As an industry, we need to reset the bar to include Java.next. Once we have, we can have a conversation about the differences in these languages. I will take up the unique aspects of the Java.next languages in future installments of this series.
Notes
- This article is taken from the first half of the JVM Language Shootout talk that I wrote for NFJS. Check the schedule for a talk near you.
- Suggestions for improving the code samples above are most welcome.
- Thanks to Justin Gehtland, Jason Rudolph, Rob Sanheim, Glenn Vanderburg, and Greg Vaughn for reading an earlier draft of this article.
Footnotes
- The
BigDecimalexample does not work as I would expect on the Scala build I have (2.7.1.final). But the important point is that I could make it work by adding an implicit conversion. I am not dependent on the language designers, I can improve the language myself.
Revisions
- 2008/08/04: fixed errata, better Clojure example for expressive collections.
Monday, August 4, 2008
This is Part One of a series of articles on Java.next. In Part One, I will explore the common ground shared by the Java.next languages.
I have chosen four languages which together represent "Java.next": Clojure, Groovy, JRuby, and Scala. At first glance, these languages are wildly different. Clojure is a Lisp. Groovy is the "almost Java" choice. JRuby has the beauty of Ruby, and the mindshare of Rails. Scala, unlike the others, brings the notion that we need more static typing.
As you might imagine, there is heated debate about which of these languages is best for some purpose, or best in general. Lost in the debate is the fact that these languages share a ton of common ground. They all evolved against a shared background, the Java language. Their design decisions are all influenced by what has worked well in Java, and what has failed.
In this article I will demonstrate two important points about the common ground these languages share:
- Over the last decade of coding in object-oriented, VM-based languages, we have learned a lot about writing expressive, maintainable applications. Java.next incorporates this knowledge, enabling essence over ceremony.
- The "essence vs. ceremony" design choices add up to a very different way of programming. The mental shift from Java to Java.next is a bigger shift than the previous shift from C/C++ to Java.
I have distilled the shared advantages of Java.next to eight points, which are explored in more detail below.
- everything is an object
- low-ceremony property definitions
- expressive collections
- functional programming
- overriding operators
- maintainable exception handling
- adding methods to existing objects
- roll-your-own constructs
Everything is an object
In Java, we live every day with the distinction between objects and primitives. This causes three practical problems:
- APIs must be duplicated: one method for objects, and another for primitives. Or worse, septlicated. One method for objects, and one each for different primitive types.
- The default (efficient, easy-to-use) numeric types have range limitations. Exceed them and your program breaks in mysterious ways.
- You cannot use intuitive math operators (+,-,etc.) with accurate numeric types.
In Java.next, everything is an object. You can invoke methods on all types using the same syntax.
; clojure
(. 1 floatValue)
1.0
// groovy
1.floatValue()
===> 1.0
# ruby
1.to_f
=> 1.0
// scala
1.floatValue
res1: Float = 1.0
Low-ceremony property definitions
In Java, to create a property, you must define a field, a getter, a setter, and (often) a constructor, all with appropriate protection modifiers. In Java.next, you can define all of these in a single step.
; clojure
(defstruct person :first-name :last-name)
// groovy
class Person {
def firstName
def lastName
}
# ruby
Person = Struct.new(:first_name, :last_name)
// scala
case class Person(firstName: String, lastName: String) {}
If you need to override (or omit) a getter, setter, or constructor for a class, you can also do that, without having to spell out all boilerplate versions of the other pieces.
And that's not all. All of these languages embrace TMTOWTDI (There's More Than One Way To Do It), so there are multiple variants on the approaches shown above.
Expressive collections
Java.next provides a convenient literal syntax for the most important collections: arrays and maps. In addition, you can string together multiple operations by passing function arguments, without having to write explicit iterators or loops. For example, to find the all the squares under 100 that are also odd:
; clojure
(filter (fn [x] (= 1 (rem x 2))) (map (fn [x] (* x x)) (range 10)))
(1 9 25 49 81)
// groovy
(1..10).collect{ it*it }.findAll { it%2 == 1}
===> [1, 9, 25, 49, 81]
# ruby
(1..10).collect{ |x| x*x }.select{ |x| x%2 == 1}
=> [1, 9, 25, 49, 81]
// scala
(1 to 10).map(x => x*x).filter(x => x%2 == 1)
res20: Seq.Projection[Int] = RangeMF(1, 9, 25, 49, 81)
There are similar conveniences for name/value collections, a.k.a. hashes or dictionaries.
Functional programming
The convenient collections described above are a special case of a more general idea: functional programming. Java.next supports functions as first class objects, allowing function arguments, functions that create new functions, and closures over the current scope. As a simple example, consider creating an adder function that adds some value chosen at runtime:
; clojure
(defn adder [x] (fn [y] (+ x y)))
// groovy
adder = { add -> { val -> val + add } }
# ruby
def adder(add)
lambda { |x| x + add }
end
// scala
def sum(a: Int)(b: Int) = a + b
Overriding operators
In Java, you cannot override operators. Math looks like this, for types like BigDecimal:
// Java math
balance.add(balance.multiply(interest));
Java.next allows you to override operators. This allows you to do create new types that feel like built-in types, e.g. you could write a ComplexNumber or RationalNumber that supports +, -, *, and /.
; Clojure
(+ balance (* balance interest))
// Groovy
balance + (balance * interest)
# JRuby
balance + (balance * interest)
// Scala (See [1])
balance + (balance * interest)
Maintainable exception handling
Checked exceptions are a failed experiment. Java code is bloated with checked exception handling code that tends to obscure intent without improving error handling. Worse yet, checked exceptions are a maintenance headache at abstraction boundaries. (New kinds of unrecoverable failures down the dependency chain should not necessitate recompilation!)
Java.next does not require you to declare checked exceptions, or to explicitly deal with checked exceptions from other code. It is a testimony to the power of Java (the platform) that other languages are free to ignore the ugliness of checked exceptions in Java (the language).
Adding methods to existing types
In Java, you cannot add methods to existing types. This leads to absurd object-mismodeling, as developers create utility classes that defy the point of OO:
// Java (from the Jakarta Commons)
public class StringUtils {
public static boolean isBlank(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
}
In Java.next, you can add methods to existing types:
; Clojure
(defn blank? [x]
(cond (string? x) (= (.length x) 0)
(nil? x) true
(throw (new Exception "time to support some new types"))))
// Groovy
String.metaClass.isBlank = {
length() == 0 || every { Character.isWhitespace(it.charAt(0)) }
}
# Ruby (from Rails)
class String
def blank?
empty? || strip.empty?
end
end
// Scala
class CharWrapper(ch: Char) {
def isWhitespace = Character.isWhitespace(ch)
}
implicit def charWrapper(ch: Character) = new CharWrapper(ch)
class BlankWrapper(s: String) {
def isBlank = s.isEmpty || s.forall(ch => ch.isWhitespace)
}
implicit def stringWrapper(s: String) = new BlankWrapper(s)
Roll-your-own constructs
In Java, you have the language and the libraries. The two are clearly distinct: you can write new libraries, but you cannot add language features.
In Java.next, the line between language and libraries is blurry. You can create new constructs that work like core language features. For example, Clojure provides an and function.
; clojure
(and 1 2) => 2
But maybe your problem domain isn't so binary. You need a most function, that returns true if most of its arguments evaluate to true. Clojure doesn't have this, but you can write one:
; clojure
(most 1 2) => true
(most 1 2 nil) => true
(most 1 nil nil) => false
The point here is not "Does my language need a 'most' conditional?" Probably not. The point is that different domains have different needs. In Java.next, the boundary between the language and the libraries is a minimized. You can adapt the language to your domain, instead of the other way around.
As another example, consider Ruby's attribute syntax:
# Ruby
class Account
attr_accessor :name
dsl_attribute :type
end
attr_accessor is built into the language. dsl_attribute is a library method that I wrote, which allows you to omit the "=" when assigning values, e.g.
# normal attributes
account.name = "foo"
# equals-free attributes
account.type checking
Conclusions
The Java.next languages share a ton of common ground. Although I've used small isolated examples for explanation, the real power comes from using these features together. Combining all the Java.next features leads to an entirely different style of coding.
- You do not have to code defensively, using a slew of factories, patterns, and dependency injection to keep your code testable and adaptable. Instead, you can build a minimal solution and evolve it.
- Instead of coding in your Java.next language, you can develop internal Domain-Specific Languages (DSLs) that better match your problem domain.
In my experience, this style of coding tends to reduce the size of a codebase by an order of magnitude, while improving readability.
Many people are looking for the "next big language." The next big language is already here, but it isn't a single language. It is the collection of ideas above (plus probably some I missed) as manifested in Java.next.
Does the transition to Java.next deserve the name "big"? Absolutely. In my experience, the move from Java to Java.next is every bit as big as the previous tectonic shifts in the industry, both in learning curve and in productivity advantages once you make the transition.
As an industry, we need to reset the bar to include Java.next. Once we have, we can have a conversation about the differences in these languages. I will take up the unique aspects of the Java.next languages in future installments of this series.
Notes
- This article is taken from the first half of the JVM Language Shootout talk that I wrote for NFJS. Check the schedule for a talk near you.
- Suggestions for improving the code samples above are most welcome.
- Thanks to Justin Gehtland, Jason Rudolph, Rob Sanheim, Glenn Vanderburg, and Greg Vaughn for reading an earlier draft of this article.
Footnotes
- The
BigDecimalexample does not work as I would expect on the Scala build I have (2.7.1.final). But the important point is that I could make it work by adding an implicit conversion. I am not dependent on the language designers, I can improve the language myself.
Revisions
- 2008/08/04: fixed errata, better Clojure example for expressive collections.