Red Hat

In Relation To Ceylon

In Relation To Ceylon

Introduction to Ceylon Part 1

Posted by    |       |    Tagged as Ceylon

This is the first installment in a series of articles introducing the Ceylon language. Note that some features of the language may change before the final release.

About Ceylon

Ceylon is a new programming language, designed to execute on the Java Virtual Machine, currently under development by my team here at Red Hat. We're fans of Java and of the Java ecosystem, of its practical orientation, of its culture of openness, of its developer community, of its roots in the world of business computing, and of its ongoing commitment to portability. However, we recognize that the language and class libraries, designed more than 15 years ago, are no longer the best foundation for a range of today's business computing problems.

Ceylon's design goals include:

  • to be easy to learn and understand for Java and C# developers,
  • to eliminate some of Java's verbosity, while retaining its readability,
  • to improve upon Java's typesafety,
  • to provide a declarative syntax for expressing hierarchical information like user interface definition, externalized data, and system configuration, thereby eliminating Java's dependence upon XML,
  • to support and encourage a more functional style of programming with immutable objects and higher-order functions,
  • to provide great support for meta-programming, thus making it easier to write frameworks, and
  • to provide built-in modularity.

Above all, Ceylon is designed to make it easy and fun to get things done in large teams.

The Ceylon compiler is not yet finished, so you can't write code in Ceylon yet. However, we would like to get the community involved in development of the language and SDK, so this serious of articles is a sneak preview for interested folks.

Let's start from the beginning.

Writing a simple program

Here's a classic example program.

void hello() { 
    writeLine("Hello, World!");

This method prints Hello, World! on the console. A toplevel method like this is just like a C function — it belongs directly to the package that contains it, it's not a member of any specific type. You don't need a receiving object to invoke a toplevel method. Instead, you can just call it like this:


Ceylon doesn't have Java-style static methods, but you can think of toplevel methods as filling the same role. The problem with static methods is that they break the block structure of the language. Ceylon has a very strict block structure — a nested block always has access to declarations in all containing blocks. This isn't the case with Java's static methods.

This method is declared using the void keyword, which means that the method declaration doesn't specify a return value. When the method is executed, it calls another toplevel method named writeLine(). This method displays its parameter on the console.

Along with the void keyword, there's also a type named Void, which is the return type of any void method, and also the root of the Ceylon type system.

doc "The root type, supertype of
     both Object (definite values) 
     and Nothing (the null value)."
shared abstract class Void() 
        of Object | Nothing {}

It might seem strange that a void method has a return type. The justification for this is that all methods in Ceylon are functions. (But not necessarily pure functions — like hello(), a Ceylon function can have side-effects.)

At a very abstract level, every method accepts arguments and returns a result. The type Void, loosely speaking, is a way of representing the notion of an unknown value of an unknown type. You can assign any value, including null, to Void, but then there's no way to get it back out again, or even discover what type of value it was. So, in theory, a void method has a return value, we just have no way of finding out anything about that value. This stuff probably doesn't sound very useful right now, but it will turn out to be useful when we discuss higher order functions and Ceylon's typesafe metamodel.

Adding inline documentation

It's usually a good idea to add some kind of documentation to important methods like hello(). One way we could do this is by using a C-style comment, either like this:

/* The classic Hello World program */ 
void hello() {
    writeLine("Hello, World!");

Or like this:

//The classic Hello World program 
void hello() {
    writeLine("Hello, World!");

But it's much better to use the doc annotation for comments that describe declarations.

doc "The classic Hello World program" 
void hello() {
    writeLine("Hello, World!");

The doc annotation contains documentation that is included in the output of the Ceylon documentation compiler. The documentation compiler will support several other annotations, including by, for specifying the author of a program element, see, for referring to related code elements, and throws, for alerting the user to exception types thrown by an executable program element.

doc "The classic Hello World program" 
by "Gavin" 
see (goodbye) 
throws (IOException)
void hello() { 
    writeLine("Hello, World!");

There's also a deprecated annotation for marking program elements that will be removed in a future version of the module.

Notice that when an annotation argument is a literal, it doesn't need to be enclosed in parentheses.

Note also that annotations like doc, by, see, and deprecated aren't keywords. They're just ordinary identifiers. The same is true for annotations which are part of the language definition: abstract, variable, shared, formal, actual, and friends. On the other hand, void is a keyword.

Strings and string interpolation

The Hello World program — wildly popular as it is — provides for a rather limited and monotonous user experience. A more typical program would produce output that sometimes varies between different runs.

Let's ask our program to tell us a little more about itself.

doc "The Hello World program 
     ... version 1.1!"
void hello() { 
    writeLine("Hello, this is Ceylon " process.languageVersion
              " running on Java " process.javaVersion "!");

Here we can see two nice things about strings in Ceylon. The first is that we can split a string across multiple lines. That's especially useful when we're writing documentation in a doc annotation. The second is that we can interpolate expressions inside a string literal. Technically, a string with expressions in it isn't really a literal anymore — it's considered a string template.

A string template must begin and end in a string literal. The following is not legal syntax:

writeLine("Hello, this is Ceylon " process.languageVersion); //compile error!

A quick fix is to terminate the template with an empty string literal:

writeLine("Hello, this is Ceylon " process.languageVersion "");

Note that this isn't the only way to concatenate strings in Ceylon. Indeed, it's only useful when you're interpolating variables or expressions into an otherwise-constant string. The + operator you're probably used to is an alternative, and more flexible in many cases:

writeLine("Hello, this is Ceylon " + process.languageVersion + 
          " running on Java " + process.javaVersion + "!");

However, don't make the mistake of thinking that these two approaches are always equivalent just because they result in the same output in this case. The + operator immediately evaluates its operand expressions and produces an immutable String object. On the other hand, a string template is an expression of type Gettable<String>, which evaluates its interpolated expressions lazily. If you print the same string template object twice, you might see different output the second time around! In fact, a string template is a kind of closure — an important concept we'll explore further in Part 2.

Dealing with objects that aren't there

An even more exciting program would receive input and produce output that depends upon the input it receives. Of course, this places somewhat more demanding requirements upon the user, but the extra work does sometimes pay off!

Therefore, this improved version of the original Hello World program takes a name as input from the command line. We have to account for the case where nothing was specified at the command line, which gives us an opportunity to explore how null values are treated in Ceylon, which is quite different to what you're probably used to in Java or C#.

doc "Print a personalized greeting" 
void hello() {
    String? name = process.arguments.first; 
    String greeting; 
    if (exists name) {
        greeting = "Hello, " name "!";
    else {
        greeting = "Hello, World!"; 

The process object has an attribute named arguments, which holds a Sequence of the program's command line arguments. The local name is initialized with the first of these arguments, if any. This local is declared to have type String?, to indicate that it may contain a null value. Then the if (exists ...) control structure is used to initialize the value of the non-null local named greeting, interpolating the value of name into the message string whenever name is not null. Finally, the message is printed to the console.

Unlike Java, locals, parameters, and attributes that may contain null values must be explicitly declared as being of optional type. And unlike other languages with typesafe null value handling, an optional type in Ceylon is not an algebraic datatype that wraps the definite value. Instead, Ceylon uses an ad-hoc union type. The syntax T|S represents the union type of T and S, a type which may contain a value of type T or of type S. An optional type is any type of form Nothing|X where X is the type of the definite value. Fortunately, Ceylon lets us abbreviate the union type Nothing|X to X?.

doc "The type of null."
shared abstract class Nothing() 
        of nothing
        extends Void() {}

The value null is the unique instance of type Nothing, but it's not an instance of Object. So there's simply no way to assign null to a local that isn't of optional type. The compiler won't let you.

doc "Represents a null reference."  
object nothing extends Nothing() {}

doc "Hides the concrete type of nothing."  
shared Nothing null = nothing;

Nor will the Ceylon compiler let you do anything dangerous with a value of type T? — that is, anything that could cause a NullPointerException in Java — without first checking that the value is not null using if (exists ... ). More formally, the if (exists ... ) construct lets us extract a value of type X from a value of type X?, thereby allowing us to invoke the members of X upon the value.

In fact, it's not even possible to use the equality operator == with an expression of optional type. You can't write if (x==null) like you can in Java. This helps avoid the undesirable behavior of == in Java where x==y evaluates to true if x and y both evaluate to null.

It's possible to declare the local name inside the if (exists ... ) condition:

String greeting; 
if (exists String name = process.arguments.first) {
    greeting = "Hello, " name "!";
else {
    greeting = "Hello, World!"; 

This is the preferred style most of the time, since we can't actually use name for anything useful outside of the if (exists ... ) construct.

Operators for handling null values

While we're discussing null values, I should tell you about an even easier way to define greeting, using the ? operator. It's just a little extra syntax sugar to save some keystrokes.

shared String greeting = "Hello, " + name?"World";

The ? operator returns its first argument if the first argument is not null, or its second argument otherwise. Its a more convenient way to handle null values in simple cases. It can even be chained:

shared String greeting = "Hello, " + nickName?name?"World";

The related ?. operator lets us call operations on optional types, always returning an optional type:

shared String shoutedGreeting = "HELLO, " + name?.uppercase?"WORLD";

Defaulted parameters

A method parameter may specify a default value.

void hello(String name="World") {
    writeLine("Hello, " name "!");

Then we don't need to specify an argument to the parameter when we call the method:

hello(); //Hello, World!
hello("JBoss"); //Hello, JBoss!

Defaulted parameters must be declared after all required parameters in the parameter list of a method.

Ceylon also supports sequenced parameters (varargs), declared using the syntax T.... We'll come back to them after we discuss sequences and for loops.

There's more...

In Part 2, we'll see how to define our own types: classes, interfaces, and objects.

Ceylon presentation: a clarification

Posted by    |       |    Tagged as Ceylon

There's a point I make in my Introducing the Ceylon Project presentation that's been widely misunderstood (perhaps mainly by people who're trying to misunderstand). Since I've been criticized over this in several venues, it merits a response. The offending statement is the following:

  • Java’s syntax is rooted in standard, everyday mathematical notion taught in high schools and used by mathematicians, engineers, and software developers
  • not the lambda calculus used only by theoretical computer scientists

Of course, now that I read this back, I admit it's quite unclear what I'm trying to say here. In my defense, this was from a presentation slide for a small conference in China that somehow wound up on slashdot. The context of the statement is a slide where I list some reasons why I think Java was a successful mainstream language for business computing. It's really quite a small point and it doesn't especially matter whether you agree with it or not.

So, a bunch of people have decided that what I'm trying to say is that somehow Java is better or more mathematical than the lambda calculus or something. Well, this interpretation doesn't even make sense. Java is a programming language. The lambda calculus is a formal system for studying the mathematical behavior of functions. So it's clear that this can't possibly be what I mean.

Now, I understand that in these days where the gotcha demagoguery and cheap-point-scoring of cable news, twitter, and blog comments have substituted for thoughtful intellectual debate, many folks aren't aware of the idea of reading arguments according to the principle of charity, that is, searching for the most charitable possible interpretation when the author's meaning is unclear. But I'm an old-fashioned type of guy, and I'm still surprised by this lack of ... hmmm ... courtesy?

Anyway, since we know - starting from the assumption that I'm not a total idiot - that I can't possibly be trying to say something totally idiotic, let's ask ourselves what is it that I was actually trying to say. (I'm afraid my real meaning will turn out a bit disappointing, after all this fuss.) The key to understanding is my use of the word syntax. Focus on that word for a second. What I mean is that Java uses a notation for function invocation, for example, f(x,y), that is taken directly from everyday mathematical notation used in engineering, science, and applied mathematics. It doesn't inherit the notation in which the lambda calculus is usually expressed, for example f x y or \x y -> x*y. I'm talking about notation. I'm not talking about the actual semantic relationship to the lambda calculus or any other mathematical concepts or formal systems.

Now, there might be perfectly good reasons why a language might want to use the notation in which the lambda calculus is usually expressed. There might be problems where this notation is simply less clunky. All I'm arguing is that it's a barrier to readability for a language intended for business or scientific computing. Again, it doesn't really matter to me if you agree or not.

Now waiting for a flood of apologies from all the folks who've called me ignorant over this. I'm looking especially at you, Scala fans ;-)

Ceylon on InfoQ

Posted by    |       |    Tagged as Ceylon

Alex Blewitt of InfoQ has published my answers to his questions about Ceylon.


Posted by    |       |    Tagged as Ceylon

So Marc Richards and then slashdot picked up on my presentations at InfoQ China. I wasn't quite expecting this level of exposure at this point, and I imagine that things will quiet down pretty quickly at least until we do an initial release of the compiler. All we have right now is a specification, an ANTLR grammar, and an incomplete type checker. Work on the backend bytecode generation is just beginning (though we'll be able to reuse a bunch of code from javac).

Nevertheless, I should make a few comments. First, I never billed this as a Java Killer or the next generation of the Java language. Not my words. Ceylon isn't Java, it's a new language that's deeply influenced by Java, designed by people who are unapologetic fans of Java. Java's not dying anytime soon, so nothing's killing it.

So why a new language? Well, we've been designing and building frameworks and libraries for Java for ten years, and we know its limitations intimately. And we're frustrated. I'm not going to recap all the frustrations here. (I've listed some of them in the first presentation.) But I guess I should mention that the number one technical problem that we simply can't solve to our satisfaction in Java - or in any other existing JVM language - is the problem of defining user interfaces and structured data using a typesafe, hierarchical syntax. Without a solution to this problem, Java remains joined at the hip to XML.

But much of our frustration is not even with the Java language itself. The extremely outdated class libraries that form the Java SE SDK are riddled with problems. Developing a great SDK is a top priority of the project.

On Slashdot, several commenters argue that creating a whole language and SDK from scratch is an enormous undertaking. Well, we're really not starting from scratch: we can reuse an enormous amount of code that is already available under open source licenses in the Java ecosystem. Just think of what's reusable from Open JDK, JBoss, and Eclipse! It's not a requirement that the entire SDK, compiler, and IDE be implemented in Ceylon. And it's not an enormous undertaking for a company of Red Hat's size. And, of course, we don't want to do this on our own. A project like this isn't even interesting unless it's a community effort.

Here's the presentations that caused this fuss:

UPDATE: If you're interested in knowing more about the language, please check out the tutorial Introduction to Ceylon.

back to top