Ceylon's Milestone 5 release showcases a promising but still young language
A few weeks back, in β7 programming languages in 7 days,β I intended to include Ceylon, but its creator Gavin King insisted it was too soon. Now Ceylon has released Milestone 5 with an HTTP package, making it possible for me to do the inevitable: Port Grannyβs Addressbook to Ceylon.
My assessment? While Ceylon is still not ready to anchor a large project, itβs worth an early look. Thereβs a lot to like about Ceylon β as well as a number of frustrating details.
[ Epic codefest: 7 programming languages in 7 days | Learn how to work smarter, not harder with InfoWorldβs roundup of all the tips and trends programmers need to know in the Developersβ Survival Guide. Download the PDF today! | Keep up with the latest developer news with InfoWorldβs Developer World newsletter. ]
The Ceylon IDE
The Ceylon IDE is essentially a set of plug-ins for Eclipse. While it works well for a milestone release, it is not entirely stable and at times freezes inexplicably. Nevertheless, those with a Java background will find it comfortably familiar. The debugger works, but imperfectly; sometimes the variables view isnβt populated or you see more of Ceylonβs underbelly than your actual variables.There is relatively good integration with the language and its features, but it lacks shine. For instance, I think the modules.ceylon file deserves its own icon or visual clue to let you know itβs special.
Modules
Speaking of modules, Ceylon integrates the concept of modules and a module repository. This is probably my favorite part of Ceylon.If youβre familiar with Javaβs Maven or Ivy, Perlβs CPAN, or Rubyβs gems, youβre familiar with modules and repositories. But Ceylon improves substantially on its Java roots. Maven modules have no real relationship to Java; theyβre simply loaded into its class path. Ceylon makes modules a first-order member of the language and its environment independent of the build system. Unlike Java, where there are always weird bugs with how Maven views the world versus the IDEβs view, this works seamlessly in Ceylon. Upgrading to a new module is simply a matter of updating your module.ceylon file.
module com.osintegrators.example.ceylon.hello '1.0.0' {
import ceylon.net '0.5';
import ceylon.dbc '0.5';
import java.jdbc '7';
import java.base '7';
}
Modules must then be imported in your runnable source code with the import statement:
import java.sql { DriverManager{ getConnection }, Connection{...}, ResultSet, Statement, PreparedStatement}
Importing
Notice that Ceylon imports a bit differently than its Java parent. There is the cosmetic issue of the bracket notation, but it goes deeper. I can import not merely classes or packages, but also functions and values. Unlike with Java, this applies not just to static methods. In fact, Ceylon does not have static methods, but any method can be imported.In the sample above, I imported the getConnection method from java.sql.DriverManager. I can now call the getConnection method directly from my own method:
Connection getDbConnection() {
String userName = "granny";
Β Β Β String password = "granny";
Β Β Β String url = "jdbc:postgresql://localhost/grannydb";
Β Β Β forName("org.postgresql.Driver");
Β Β Β return getConnection (url, userName, password);
}
Naming conflicts between packages are a frustrating problem in Java. One of the most common in the early days of Java was java.util.Date and java.sql.Date. In the case of importing both in the same class, you always had to refer to them by their fully qualified name. Ceylon imports allow you to use aliases instead.
import java.sql { SqlDate=Date }
import java.util { Date }
Java language integration
You may notice that I imported some Java packages above. Ceylon doesnβt rebuild the Java universe (at the moment). Iβm using Javaβs JDBC support directly. For the purpose of demonstrating Ceylon, I use the old-school Java DriverManager and JDBC driver. I could have wired up a connection pool, but letβs face it β Granny wonβt need scalability for her address book.When I use Java from Ceylon, I get most of the features of Ceylon classes and functions even if they are really Java classes and methods. I can import individual methods, alias items, and more. One caveat: This sometimes means little conflicts with the type system. In particular, a lot of Java stuff returns collections that appear to be the same as the Ceylon collections, but are not. I accidentally used a Java ArrayList and wondered why I couldnβt run it in a Ceylon for/each statement. For Ceylon to iterate over it, it needed to support one of Ceylonβs interfaces.
It may sound contradictory, but while Java integration is one of Ceylonβs best features, itβs where I messed up the most. When typing familiar Java APIs in a different syntax, when the compiler says no, I have trouble βthinkingβ in Ceylon when typing Java APIs.
JavaScript integration
In the interest of time, I reused an existing JavaScript front end from the β7 programming languages in 7 daysβ article. In theory, I could have written the front end in Ceylon. This is the best argument against the idea that the future might be written entirely in JavaScript β and in support of the idea that JavaScript is the assembly language of the future. You can write Ceylon and target Node.js, the browser, or possibly any other JavaScript environment.Typing in Ceylon
Ceylon is a strongly and statically typed language like Java. In fact, it is more strongly and arguably more statically typed than Java. The one βlooseningβ is that types do not have to be declared on references in advance of their first use. For example, I could type:
value header = contentType("text/html", utf8);
After this line the value reference header is a Ceylon string type. Notice I said βvalue referenceβ β itβs essentially the same as final variables in Java. They cannot be changed. If I wanted a reference that could be changed, Iβd type:
variable header = contentType("text/html", utf8);
A bigger issue with typing is the way nulls are handled. It would be very difficult to get a NullPointerException in Ceylon. The type system requires you to explicitly handle nulls, which are, in essence, a type themselves.
Ceylon gives you tools to do that, but they may not be entirely intuitive while you are simultaneously thinking in Java or JavaScript. If I wanted to allow nulls, I could type a value like this:
Contact? contact = getContact(id);
someMethodThatDoesntHandleNulls(contact); //compile error
If I wanted to pass that to a method that doesnβt handle nulls, I would need to use the exists keyword. For example, I could do this:
Contact? contact = getContact(id);
if (exists contact) {
Β someMethodThatDoesntHandleNulls(contact);
} else {
Β doSomethingElse();
}
Alternatively, I can use else like this:
String name = request.parameter("contact[name]") else "";
Ceylon actually supports multiple types for the same reference. For instance, I can have a type that is both Contact|Person. In fact Contact? is actually a shorthand for Contact|Null.
An emerging SDK
While porting Granny to Ceylon, I ran into multiple bugs; granted, most of these were in the brand-new HTTP server module. Commendably, these were all fixed very quickly by the Ceylon developer community β in some cases, within an hour of reporting the bug. However, these are not edge cases; they are basic tasks like not hanging the thread after each delete request and handlingcontentType headers that contain the character set. When I say this is a βfirst lookβ and Ceylon is not ready to be the basis for your mission-critical project, I really mean it β even if youβre a Hipster Hacker.The Ceylon APIs also presently lacks the superintuitiveness of, say, Ruby. For instance:
AsynchronousEndpoint {
Β Β Β path = Β startsWith("/scripts.js");
Β Β Β service = serveStaticFile(".");
}
The argument to serveStaticFile is the relative path to prepend in front of whatever is in path. That is, the path is the argument to the thing with File and the file is what is in path.
Also, both strings and iterables have βlastβ members. In the case of a string, it refers to the last character. In the case of an iterable, this refers to the last element. That isnβt overly confusing, unless you think of some of the places you might use them together, such as a split function. I swapped a split for a span, and suddenly I had type errors telling me I couldnβt parse a character as an integer. It took a while to find the errant last.
Moreover, there is a matcher (above) on path but not on βmethodβ forcing me to write something like this:
void contacts(Request request, Response response) {
if(request.method.equals("GET") &&
Β Β Β Β Β Β Β request.path.equals("/contacts")) {
contactsGet(request, response);
} else if (request.method.equals("GET") &&
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β request.path.startsWith("/contacts")){
contactGet(request, response);
} else if (request.method.equals("POST")) {
contactsPost(request, response);
} else if (request.method.equals("DELETE")) {
contactDelete(request response);
} else if (request.method.equals("PUT")) {
contactPut(request, response);
} else {
}
}
This is especially glaring in a language thatβs generally less verbose yet clearer. More important, this is the kind of code monkey stuff I hate to write β and why we have interns.
What you see is the kind of minor API design that will happen with a few people using it. This will most certainly happen as Ceylon matures.
Features
Ceylon has a lot of nice features, from higher-order functions to function references. It has solid typing stuff like unions and intersections. I started to implement some of these into Granny, but thought better of it. My longestwhile statement comprises one line. I could have made a higher-order function, but would it be more intuitive or clearer? If I was doing something more than creating an object from a result set, maybe. In this case, no.Conclusion
Personally, I categorize languages based on where you pay the pain. In JavaScript, for instance, I have a very short learning curve and rapid development. On a large JavaScript project, I start paying the pain at the end of the project while debugging.For the uninitiated, Ceylon makes you take the pain up front, especially in getting used to its type system and the items it eliminates. However, I can already see it would probably pay it forward on my first debugging session with a larger project.
Moreover, if youβve ever wanted to get involved in open source, these folks are some of the more helpful and motivated Iβve encountered. Many developers try their hand at mature projects and find there is no low-hanging fruit. This is your chance to work on something where people are motivated and helpful, and thereβs enough to be done that a new recruit can get involved easily.
Ceylon isnβt ready for prime time yet, but itβs very promising. Grannyβs Addressbook works in Ceylon (with a few caveats). Ceylon has a lot of great features, but many of them arenβt needed for such a simple application. Still, use of Ceylonβs more involved features made my code shorter, more concise, and easier to read. I look forward to seeing Ceylon develop.
This article, βA first look at Gavin Kingβs Ceylon,β was originally published at InfoWorld.com. Keep up on the latest developments in application development and read more of Andrew Oliverβs Strategic Developer blog at InfoWorld.com. For the latest business technology news, follow InfoWorld.com on Twitter.


