Ceylon: True advance, or just another language?

Object-oriented and functional programming for the masses

Linux and open source are commonly associated with the cutting edge of language design. It may be the available tools that support language development or the openness of the platform that gives rise to the advancement of language design. Or perhaps it's that open languages (such as the GNU Compiler Collection family, Ruby, Python, and Perl) based on open source technologies are great because they invite and encourage experimentation and use (not to mention that Red Hat is the company behind Ceylon). For whatever reason, Linux developers have access to a wide range of languages, from the lesser-used historical languages to the newest, cutting-edge offerings.

But in a world of C/C++, the Java™ language, Scala, Ruby, Python, Perl, Erlang, Lua, Scheme, and so many others, should we care about the announcement of a new language focused on business-oriented enterprise software development? In many cases, the answer is no, but let's explore Red Hat's future language offering, called Ceylon, to see whether it can rise to the ranks of today's most popular languages.

Introducing Ceylon

Ceylon is a new project out of Red Hat led by Gavin King. King is the founder of the Hibernate project, a persistence solution within the Java language. Although King is a fan of Java technology—it was one of the first languages suitable for large-scale development—he is quoted as having a number of frustrations with the language (including language complexities like generics, the hastily designed and obscure Standard Edition SDK, a clumsy annotation syntax, broken block structure, dependence on XML, and more).

So, King asked the question, what would a language look like with the lessons learned from the advantages and disadvantages of the Java language and SDK? His answer is Ceylon, a statically typed language that retains some of the best features of the Java language (and runs on the JVM) but improves on that language's readability, built-in modularity, and the incorporation of functional language features like high-order functions. Ceylon also incorporates features of C and Smalltalk. Much like the Java language, this new language is focused on business computing, but it is also flexible and useful in other domains.

Some have called Ceylon a "Java killer" (perhaps due to questions of the Java language's future), but Ceylon in fact runs on the JVM, so it's an extension of Java technology rather than a replacement. Using the JVM to support execution of Ceylon is an ideal model, because it means that Ceylon (like Java) is portable across the multitude of architectures that currently support the JVM.

Ceylon language features

Most languages today defy a simple categorization and instead represent a variety of programming styles. Ceylon is no different. Ceylon is a statically typed language (which means that type checking is performed at compile time, compared with dynamically type languages such as Lisp, where type checking is performed at run time). Ceylon is an object-oriented language, like the Java language, and also supports higher-order functions (which means that functions can take functions as input or output) with a typical C syntax style. Higher-order functions are not supported directly in the Java language, so this functionality represents a unique difference in the two languages.

Sometimes, however, improvements are more about what a language removes than what it adds. Ceylon simplifies and removes elements of the Java language, replacing them with a simpler scheme. One example of a simplification is the removal of the public, protected, and private keywords. Instead, Ceylon simply includes the shared annotation, which defines which elements of a class are visible externally. Ceylon also removes the ability to overload but provides some workarounds for this functionality (such as defaulted and sequenced parameters) with simpler syntax.

Ceylon includes support for inheritance, sequences (array or list construct), generics, named arguments, and more. It includes features for run time type management (we'll explore an example of this in the next section). The language is under active development, so the final feature set remains open.

Ceylon illustrated

Although at the time of this writing a publicly usable compiler does not yet exist, the structure of the Ceylon language is defined, allowing development of sample applications to explore and reason about its use and readability. This section looks at some sample applications in Ceylon that illustrate its structure.

Hello World

I'll use the "Hello World" program to illustrate the creation of a simple program to emit a simple text string to the display. The example shown in Listing 1 shows a top-level method called hello, which uses the writeLine method to emit a string to standard output.

Listing 1. The Hello World program in Ceylon
doc "Hello World Program"
by "Gavin King"
void hello() {
  writeLine( "Hello World." );

Note also the annotation used for API documentation (similar to tools like doxygen), which allows you to specify the method and author (the doc and by annotations, respectively).

Ceylon types

Ceylon incorporates a traditional set of types that are implemented as ordinary classes. These types are:

  • Natural. Unsigned integers, including zero
  • Integer. Signed integers
  • Float. Floating point
  • Whole. Arbitrary precision signed integers
  • Decimal. Arbitrary precision and arbitrary scale decimals

By default, the Natural, Integer, and Float types are 64 bit, but you can annotate them with small to specify 32-bit precision.

Ceylon class

As Ceylon is an object-oriented language, you write code using the concept of classes. A class is a type in Ceylon that encapsulates a set of operations (called methods) and state in addition to a definition of how the state is initialized when an object of the class is initialized (the class initializer, similar to the constructor).

A simple class will help you understand Ceylon's approach. Listing 2 provides a simple class for a counter class. Line 2 defines the class with an optional value, which means that the user can provide it or not, and it is denoted with the Type? pattern. Instead of a constructor, the body of the class contains the class initializer. This code defines the private variable (nothing is visible unless annotated as shared), and then the initialization logic. You begin by checking to see whether the start variable exists. If it does, it's used as the initial value for your count. Your first method, annotated as shared and therefore visible externally from the class, defines the incrementer. When called, this method simply increments your counter.

Finally, you define a getter method that returns the current counter value to the user and a setter method that sets the current counter value with one provided by the caller. Note the use of the assign keyword here to create a variable attribute for setting the counter's value. In addition to handling constructors differently (code embedded within the class), there's no deconstructor and no way to implement multiple constructors (just one of the differences from the Java language).

Listing 2. Simple class in Ceylon
01    doc "Simple Counting Class"
02    class Counter( Natural? start ) {
04      doc "Class Initializer"
05      variable Natural count := 0;
06      if (exists start) {
07        count := start;
09      }
11      doc "The incrementer"
12      shared void increment() {
13        count++;
14      }
16      doc "The getter"
17      shared Natural currentValue {
18        return count;
19      }
21      doc "The setter"
22      shared assign currentValue {
23        count := currentValue;
24      }
26    }

With your simple class defined, let's look at how you use the class in Ceylon. Listing 3 provides a block of code that uses your Counter class. It begins with the instantiation of the class to the cnt object. Note that there is no new keyword in Ceylon. With your new Counter object defined, you call the increment method and then emit the Counter value using your getter method. Note that the = and := operators are different in Ceylon: you use the = specifier only for immutable values, whereas variable assignment is performed with the := operator.

Listing 3. Using the Counter class
01    Counter cnt = Counter(1);
02    cnt.increment();
03    writeLine( c.currentValue );

Ceylon encourages the use of immutable attributes whenever possible. This means that an object is initialized with a value and not reassigned. To specify that a named value is mutable (can be changed after initialization), it must be annotated with variable, as is shown in Listing 2 at line 5.

One final element to explore is a key difference in control structures in Ceylon. Note that in many languages, braces ({}) can be omitted after a conditional expression, such as if a single statement appears:

if (cnt > 10) statement();

Ceylon forbids this syntax and requires that braces be present. This means that the sample code shown above must be written in Ceylon as:

if (cnt > 100) { statement(); }

As this represents one of the most common errors in C, it's a welcome addition to force this kind of proper style.

Higher-order functions

Ceylon includes the functional style of programming called first-order functions. This simply means that functions are treated as first-class objects and can be used as parameters to functions as well as be returned from functions. Take the example from King's presentation for the definition of the repeat method (see Listing 4). In this case, it takes two arguments: a Natural for the number of times to repeat and a method argument for the function to call. Within the body of the repeat method, you simply create a for loop (using a range operation) and call the method as passed as the functional parameter.

Listing 4. Higher-order functions in Ceylon
01    void repeat( Natural times, void hfunction() ) {
02      for (Natural n in 1..times) {
03        hfunction();
04      }
05    }

Using this method is simple, as shown in line 7 of Listing 5. As shown, the method's name is used without arguments.

Listing 5. Using higher-order functions in Ceylon
01    void sayhello() {
02      writeLine( "Hello World." );
03    }
05    ...
07      repeat( 10, sayhello );

Unlike other languages with functional support, Ceylon doesn't support anonymous functions (unnamed functions that appear directly in expressions). It does include support for closures (which are essentially functions that can reference state in another function).

Type narrowing

Ceylon does not include the instanceof operator found in the Java language; nor does it include typecasting, as can be found in C. Instead, Ceylon implements what is called type narrowing, which is used to test and narrow the type of an object reference in one step. Consider the following code segment in Listing 6. This code uses a special (is ... ) construct to test an object reference to a given type. Once the type is identified, the type-specific method is then used. This construct is similar to the (exists ...) construct that you saw earlier in Listing 2 for optional parameters.

Listing 6. Type narrowing in Ceylon
01    Object obj = <some object>;
03    switch (obj)
05      case (is Counter) {
06        obj.increment();
07      }
08      case (is ComplexCounter) {
09        obj.incrementBy(1);
10      }
11      else {
12        stream.writeLine("Unknown object");
13      }

Ceylon includes another, similar construct defined as (nonempty ...), which you can apply to sequences (arrays or lists) to determine whether the sequence contains no elements and therefore may not have sequence operations applied to it.

Finally, note the syntax for switch statements in Ceylon, which differs from both C and the Java language. Where those two languages can be error prone, Ceylon forces a block structure on the cases and removes the default case in preference to an else block. Ceylon also ensures (at compile time) that the switch statement contains an exhaustive list of instance tests or, at a minimum, an else clause to provide complete coverage. The compiler automatically checks these switch statements and generates an error if an instance is not covered.

Other control structures

Ceylon implements the traditional if...else statements, as you would expect, and also implements the Java language's exception handling features (try, catch, finally). Ceylon also creates what is called a fail block that is used with for loops to identify when they do not break prematurely. Consider the example shown in Listing 7.

Listing 7. Illustrating Ceylon's fail block
01 for (Instrument i in instruments) {
02   if (i.failing()) {
03     break;
04   }
05 }
06 fail {
07   // All instruments are working...
08 }

This is a common design pattern in both C and the Java language and therefore a useful addition to Ceylon.

Ceylon's future

As King has said, Ceylon is a community effort and therefore needs software engineers and testers to help design, build, and validate the language and SDK. This call could encourage feedback from Java language users to help support their migration from that language to Ceylon. King is still somewhat silent on the current status of Ceylon, saying only that a language specification exists as well as ANTLR (Another Tool for Language Recognition) grammar.

Going forward

Although some may challenge the necessity of a new language, another way to view languages is as a set of tools that can be used to solve problems. Not every language is suitable or ideal for any given problem, but certain languages lend themselves nicely to specific solution domains; therefore, the availability of multiple languages is a blessing, not a curse. Because Ceylon is still under development, it's unknown whether it will find a place in the ranks of popular languages in use today. But the language captures enough interesting features that it will be fun to explore further when it finally appears.

Downloadable resources

Related topics

  • Gavin King's original introduction to the Ceylon language can be seen in this 30-minute video titled Introducing the Ceylon Project Video. Two presentations exist for Ceylon from King, including the introductory presentation and one titled The Ceylon Type System, which covers some of its more advanced features.
  • Gavin King provides an 11-part series that describes the Ceylon language. As you'll see from the comments on each element of the series, there's a considerable amount of feedback and a dynamic tailoring of the language to remedy issues that are found. You can find this series at King's blog. To participate in the construction of Ceylon, contact Gavin King at gavin at hibernate dot org.
  • ANTLR is a language tool and framework that's used to build compilers, interpreters, translators, and recognizers from a grammatical description. ANTLR comes from the University of San Francisco, based on work from Terence Parr.
  • This article touched on some of the functional programming features implemented in Ceylon. Check out the introductions of first-class functions, higher-order functions, and closures at Wikipedia.
  • In the developerWorks Linux zone, find hundreds of how-to articles and tutorials, as well as downloads, discussion forums, and a wealth of other resources for Linux developers and administrators.
  • Start developing with product trials, free downloads, and IBM Bluemix services.
  • Follow developerWorks on Twitter.
Zone=Linux, Open source
ArticleTitle=Ceylon: True advance, or just another language?