Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.

The Adventurous Developer’s Guide to JVM languages – Ceylon

Last week Simon challenged me with his own implementation of HTTP server in Kotlin. Challenge accepted! So I picked Ceylon, the programming language developed by Red Hat and tried to implement the same. Ceylon is fairly new programming language and haven’t reached its final 1.0 version yet.


As a Java developer, I expected to to be able to adopt Ceylon very quickly, and in some sense I succeeded. Basically, to start coding it is enough to walk through the Tour of Ceylon at the project homepage – you will get most of the information about the language from there.

The Ceylon source code for the HTTP server that I have written for this experiment is available at GitHub. Feel free to comment, suggest and submit pull requests – I’ll be happy to learn from you.

First impressions

The very first thing we will notice about Сeylon is that its “infrastructural” part is very well prepared. By this I mean modules. Ceylon runtime is based on JBoss Modules and Ceylon uses modules for everything. Even your brand new project is actually a Ceylon module, which is declared via module.ceylon file in the project directory:

module com.zt '1.0.0' {
  import '0.4.1';
  import java.base '7';

This means that my module is called com.zt and its version is 1.0.0. One thing to notice about the module declaration format is that the module name is not in quotes and the version is in quotes, e.g. ‘1.0.0’. That is a bit strange since the version is a number and package isn’t. Why not to skip the quotes for the version? Probably because version might contain non-numeric characters, like 1.0.0-SNAPSHOT. But then I would skip the quotes all for good.

Next, the main file of the project is run.ceylon and we can just implement run() method to launch our app:

void run(){
   print("Hello from Ceylon!");

Methods (or functions) in Ceylon can be standalone and do not belong to any class. Just like in Kotlin. Same applies to attributes. Actually, I was curious what it compiles to. What I learned is that every standalone attribute (or field) and method are compiled into a dedicated class. That means that the following code is compiled into 2 classes – port_.class and run_.class – which will also include the public static void main method.

import { ServerSocket, Socket }
import java.lang { Thread }

shared Integer port = 8080;

void run(){
   ServerSocket server = ServerSocket(port);
   print("Listening for connections on port " port "...");
   	Socket socket = server.accept();
   	print("New client connection accepted!");
   	HTTPServer httpServer = HTTPServer(socket);
   	Thread threadRunner = Thread(httpServer);

When I was trying to understand this part I discovered that Ceylon tooling compiles and packages everything into Ceylon ARchive (car) and cleans up after itself, so you won’t find class files after compilation – I had to unpack the archive to get access to the class files.

One nice part of the tooling was that when I added a new dependency into module.ceylon file the dependency was automatically downloaded from Ceylon Herd and resolved by IDE. That’s another win from the infrastructure of the language.

Java Interoperability

Java interoperability was a tricky part for me. First of all, you have to add dependency into module.ceylon to be able to use Java classes: import java.base '7';

When you’re using Java classes in Ceylon, intuitively you would expect all the same methods to be available on the Java class instance as if you would normally use it from Java program. In Ceylon it is not like that. Here’s an example:

value printWriter = PrintWriter(socket.outputStream);

I would expect to be able to use getOutputStream() method on socket, but Ceylon interprets it as a getter for a field and replaces its semantics with a property access (perhaps?). Even if getOutputStream() isn’t really a getter, if you check its source.


The second challenge I encountered was about type conversion. There’s a special mapping between Ceylon types and Java types.

So fileData must be of a byte array type, and according to the type mapping rules it should be declared as Array<Integer> in Ceylon. OK, so did:

Array<Integer> fileData = arrayOfSize { size = fileLength; element = 0; };

Oh my.. no luck:

Exception in thread "Thread-0" java.lang.ClassCastException: [J cannot be cast to [B

Apparently, for type conversion to work correctly I needed to import special Java interoperability module: import ‘0.4.1’, and then use a special method provided by this module. Here’s the code that I needed the type conversion for:

Array<Integer> fileData = createByteArray(fileLength);
value fileIn = FileInputStream(file);;

Basically, after all this issues I’ve got my HTTP server implementation running but there was once more surprise ahead waiting for me at runtime: null checks.

When I’ve got my application running and submitted the first GET request, it failed with the following stack trace.

Exception in thread "Thread-1" java.lang.NullPointerException

The surprising part was that at classes.ceylon:19 I had the following line for code:

String input = bufferedReader.readLine();

This is where some knowledge of how to read decompiled code using javap helped me. The bytecode revealed that Ceylon compiler inserts null checks after the returns from methods of Java classes. I had to use '?' suffix for the declaration to fix that:

String? input = bufferedReader.readLine();

But then there’s a problem of how this value is used. For instance, if it is used as a parameter for StringTokenizer, then instantiating the class would fail as well. So I just made a lame fix to make it work:

if(!exists input){ 

I’m sure there should be a better way on how to handle this situation despite it solved my immediate problem.

What did I learn?

The syntax and the keywords of Ceylon are quite different from the ones that I’m used to in Java. However, it did not disturb me that much. In most cases it was enough to associate the keyword from Ceylon with the one form Java, for instance, satisfies in Ceylon vs implements in Java.

Overall, it was quite an interesting experience. The impression I’ve got from Ceylon is that it is focused on the style of OOP and infrastructure (module system) rather than on conciseness of the expressions, not trying to squeeze a million of meanings into a one-liner. It actually means that Java verbosity is not something that Ceylon creators would be worried about, it is rather the ecosystem and language design at large that is a focus.

Psst! If you liked this post, we wrote a 50-page RebelLabs report on Java 8, Scala, Groovy, Fantom, Clojure, Ceylon, Kotlin & Xtend.

[publication id=”48″ label=”Get the FULL REPORT on all 8 JVM languages”]