Blog of Gavin King

Ceylon 1.0.0 is now available

Today, we're proud to announce the first production release of the Ceylon language specification, compiler, and IDE. Ceylon 1.0 is a modern, modular, statically typed programming language for the Java and JavaScript virtual machines.

Ceylon enables the development of cross-platform modules that execute portably in both virtual machine environments. Alternatively, a Ceylon module may target one or the other platform, in which case it may interoperate with native code written for the platform.

In the box

This release includes:

  • a complete formal language specification that defines the syntax and semantics of Ceylon in language accessible to the professional developer,
  • a command line toolset including compilers for Java and JavaScript, a documentation compiler, and support for executing modular programs on the JVM and Node.js,
  • a powerful module architecture for code organization, dependency management, and module isolation at runtime,
  • the language module, our minimal, cross-platform foundation of the Ceylon SDK, and
  • a full-featured Eclipse-based integrated development environment.

ide screenshot

Language

Ceylon is a highly understandable object-oriented language with static typing. The language features:

  • an emphasis upon readability and a strong bias toward omission or elimination of potentially-harmful constructs,
  • an extremely powerful type system combining subtype and parametric polymorphism with declaration-site variance, including first-class union and intersection types, and using principal types for local type inference and flow-sensitive typing,
  • a unique treatment of function and tuple types, enabling powerful abstractions,
  • first-class constructs for defining modules and dependencies between modules,
  • a very flexible syntax including comprehensions and support for expressing tree-like structures, and
  • fully-reified generic types, on both the JVM and JavaScript virtual machines, and a unique typesafe metamodel.

More information about these language features may be found in the feature list and quick introduction.

This release does not introduce new language features. However, a very large number of bugs have been fixed.

IDE

Screenshots of the IDE may be seen here.

This release of the IDE introduces the following new features:

  • a type hierarchy view, to complement the popup type hierarchy,
  • a documentation view, to complement the documentation hover,
  • a new Ceylon Module properties page, and the ability to manage dependencies from this page or from the New Ceylon Module wizard,
  • enhancements to the search results view,
  • improvements to syntax highlighting in the hover,
  • a much improved wizard for importing Java archives into Ceylon module repositories, and
  • many bugfixes.

Community module repository

Ceylon Herd is now open to the public.

herd screenshot

SDK

The platform modules, recompiled for 1.0.0, are available in the shared community repository, Ceylon Herd.

This release introduces the following new platform modules:

  • ceylon.build, a framework for writing build scripts in Ceylon, and
  • ceylon.html, a library for defining HTML content in Ceylon.

Community

The Ceylon community site, http://ceylon-lang.org, includes documentation, and information about getting involved.

Source code

The source code for Ceylon, its specification, and its website is freely available from GitHub.

Issues

Bugs and suggestions may be reported in GitHub's issue tracker.

Acknowledgement

We're deeply indebted to the community volunteers who contributed a substantial part of the current Ceylon codebase, working in their own spare time. The following people have contributed to this release:

Gavin King, Stéphane Épardaud, Tako Schotanus, Emmanuel Bernard, Tom Bentley, Aleš Justin, David Festal, Flavio Oliveri, Max Rydahl Andersen, Mladen Turk, James Cobb, Tomáš Hradec, Michael Brackx, Ross Tate, Ivo Kasiuk, Enrique Zamudio, Roland Tepp, Diego Coronel, Daniel Rochetti, Loic Rouchon, Matej Lazar, Corbin Uselton, Akber Choudhry, Lucas Werkmeister, Julien Viet, Brent Douglas, Lukas Eder, Markus Rydh, Julien Ponge, Pete Muir, Henning Burdack, Nicolas Leroux, Brett Cannon, Geoffrey De Smet, Guillaume Lours, Gunnar Morling, Jeff Parsons, Jesse Sightler, Oleg Kulikov, Raimund Klein, Sergej Koščejev, Chris Marshall, Simon Thum, Maia Kozheva, Shelby, Aslak Knutsen, Fabien Meurisse, Paco Soberón, Sjur Bakka, Xavier Coulon, Ari Kast, Dan Allen, Deniz Türkoglu, F. Meurisse, Jean-Charles Roger, Johannes Lehmann, Nikolay Tsankov.

New Road Map

With the release of 1.0 beta, we reached the end of our previous roadmap. This doesn't mean we've run out of work to do, so of course we needed a new one. Here's a sketch of our plan:

Ceylon 1.0 final

The focus of development right now is of course bug fixes to the compiler backends, language module, and IDE. The major new feature planned for 1.0 final is serialization, which, even though we think of it as a language feature, is really, strictly speaking, a feature of the language module.

In parallel, David Festal is working on some very-needed optimizations to build performance in Ceylon IDE, though that work won't be finished in 1.0 final. I found the time this weekend to squeeze a couple of cool new features into the IDE, but that's not supposed to be the priority for this release.

There's also some issues we need to work through with interop between our module system and Maven. I'm not sure whether we'll be able to get this all sorted out in 1.0, or whether this will slip (again) to 1.1.

Finally, we hope to have a couple of new platform modules ready for the 1.0 final release, including ceylon.transaction.

Ceylon 1.1

Ceylon 1.1 will be all about performance, including language performance, compiler performance, and David's ongoing work on IDE build performance. There are a whole bunch of really obvious optimizations we can make, that we simply havn't had time to work on yet.

A warning: we expect to break binary compatibility between 1.0 and 1.1. That's not something we do lightly, and it's not something we plan to make a habit of. Changes affecting binary compatibility should occur in major releases, not minor releases. Please forgive us if we break our own rule this one time.

At the same time, building out the Ceylon SDK will also be a top priority. This will probably be what I personally focus on for a while.

Ceylon 1.2

Ceylon 1.2 will introduce a number of new language features. We're not completely sure of precisely which new features will make the cut, though we do have a shortlist of several which are almost certain to make it in. I'm not talking about major new features of the type system or anything here. We're really very happy with what's possible in the language as it exists today. The priority will be additional syntax for dealing with situations that are a little uncomfortable right now, and removal of a handful of limitations introduced temporarily for 1.0.

Here's a list of some of the things under very serious consideration for 1.2. Don't expect all of them to make the cut. We want to make the language easy to use. That means fixing things that suck without bloating out the language with too many overlapping features and minor syntax variations.

Ceylon 1.0 beta

After more than three years of development, Ceylon is now feature-complete. Ceylon 1.0 beta implements the whole language specification, providing the capability to execute Ceylon programs on both Java and JavaScript virtual machines and to interoperate with native code written for those platforms. This release includes:

  • a complete formal language specification that defines the syntax and semantics of Ceylon in language accessible to the professional developer,
  • a command line toolset including compilers for Java and JavaScript, a documentation compiler, and support for executing modular programs on the JVM and Node.js,
  • a powerful module architecture for code organization, dependency management, and module isolation at runtime, and
  • the language module, our minimal, cross-platform foundation of the Ceylon SDK.

Simultaneously, we're releasing Ceylon IDE 1.0 beta, the latest iteration of our full-featured Eclipse-based development environment.

runas

New features of the language

This release introduces the following new language features, along with many bugfixes:

  • annotations and annotation constraints,
  • a typesafe metamodel,
  • "static" method and attribute references,
  • try with resources,
  • support for strings, integers, and characters in switch,
  • support for named unicode characters in string and character literals,
  • the ** scaling multiplication operator,
  • nonempty variadic parameters, and
  • a new improved syntax for calling concrete members of inherited interfaces.

The new features are summarized here.

metamodel

New features of the IDE

This release of the IDE introduces performance improvements, many bugfixes, and:

  • support for launching Ceylon programs on the module runtime,
  • paste-with-imports, and autoindentation on paste,
  • integration with Eclipse's built-in file and package refactorings,
  • inline "linked-mode" rename, and rename support for references in documentation strings,
  • improvements to autocompletion, including "linked-mode" argument completion,
  • much improved integration for Eclipse's merge viewer,
  • integration with the commandline toolset's configuration file format,
  • several new quick fixes and assists, including new quick assists for adding and changing import aliases, and
  • a new editor preferences page.

You can see screenshots of the new release here.

autocomplete

rename

Community

The Ceylon community site includes documentation, and information about getting involved.

http://ceylon-lang.org

SDK

The platform modules, recompiled for 1.0 beta, are available in the shared community repository, Ceylon Herd.

https://herd.ceylon-lang.org

Source code

The source code for Ceylon, its specification, and its website, is freely available from GitHub:

https://github.com/ceylon

Issues

Bugs and suggestions may be reported in GitHub's issue tracker.

Acknowledgement

We're deeply indebted to the community volunteers who contributed a substantial part of the current Ceylon codebase, working in their own spare time. The following people have contributed to this release:

Gavin King, Stéphane Épardaud, Tako Schotanus, Emmanuel Bernard, Tom Bentley, Aleš Justin, David Festal, Flavio Oliveri, Max Rydahl Andersen, Mladen Turk, James Cobb, Tomáš Hradec, Michael Brackx, Ross Tate, Ivo Kasiuk, Enrique Zamudio, Roland Tepp, Diego Coronel, Brent Douglas, Corbin Uselton, Loic Rouchon, Lukas Eder, Markus Rydh, Matej Lazar, Julien Ponge, Julien Viet, Pete Muir, Nicolas Leroux, Brett Cannon, Geoffrey De Smet, Guillaume Lours, Gunnar Morling, Jeff Parsons, Jesse Sightler, Oleg Kulikov, Raimund Klein, Sergej Koščejev, Chris Marshall, Simon Thum, Maia Kozheva, Shelby, Aslak Knutsen, Fabien Meurisse, Paco Soberón, Sjur Bakka, Xavier Coulon, Akber Choudhry, Ari Kast, Dan Allen, Deniz Türkoglu, F. Meurisse, Jean-Charles Roger, Johannes Lehmann.

Ceylon M6 progress report

So it looks like it's time again for me to offer my usual lame excuses for another late milestone release of Ceylon. Well, I suppose all that really matters is: it's coming soon!

Ceylon M6 will be the first feature-complete implementation of the language specification, incorporating the following headline changes:

  • new syntax for invoking super-interface members,
  • nonempty variadic parameters
  • try with resources,
  • the ** scaling multiplication operator,
  • "static" member references,
  • metamodel and metamodel expressions, and
  • annotations.

We have not yet decided if support for serialization will make it into M6.

Invoking super-interface members

Previously, we were using a rather ugly and arbitrary syntax, for example, List::equals(that), to invoke an overridden member of a superinterface. Now we can just write:

super.equals(that)

except in cases where this is ambiguous (the member is ambiguously inherited from more than one supertype), in which case we can use the widening of operator to eliminate the ambiguity:

(super of List<T>).equals(that)

(We now treat super as a value whose type is the intersection of all immediate supertypes of the current type.)

Nonempty variadic parameters

You may now define a variadic function that requires at least one argument, for example:

String max(String+ strings) {
    value max = strings.first;
    for (string in strings.rest) {
        if (string>max) {
            max = string;
        }
    }
    return max;
}

The type of such a function is written String(String+), meaning, of course, Callable<String,[String+]>.

try with resources

This constuct works almost exactly like in Java. For example:

try (Transaction()) { ... }

Scaling multiplication operator

A top user request was support for "scaling" multiplication for vectors, matrices, durations, etc. We've introduced the interface Scalable and the ** operation to let you write things like:

Vector scaled = 2 ** vector;

Static member references

Static member references let us obtain a reference to a member of a type without providing an instance of the type. For example:

  • Person.name refers to the attribute name declared by the class Person, and
  • Object.equals refers to the method equals declared by the class Object.

The type of a static method reference is a function type where the first parameter list accepts an instance of the type that declares the member.

  • Person.name is of type String(Person), and
  • Object.equals is of type Boolean(Object)(Object).

Static attribute references are especially useful, since we can pass them directly to map():

Person[] people = .... ;
{String*} names = people.map(Person.name);

This functionality is already working.

The metamodel

We call Ceylon a "higher-order" language partly because functions, classes, methods, and attributes are typed values. For example, the class Person is a value of type Person(Name):

Person(Name) createPerson = Person;
Person person = createPerson(Name("Gavin", "King"));

However, Ceylon takes the notion of "higher-order" significantly further. The language lets us write typesafe code that reasons about the program itself, by providing a typesafe metamodel.

A metamodel expression is enclosed in backticks:

Class<Person,[Name]> personClass = `Person`;
Attribute<Person,Name> nameAttribute = `Person.name`;
Method<Object,Boolean,[Object]> equalsMethod = `Object.equals`;

We can use a metamodel to obtain the name and annotations of a declaration, or to query a type for members by their return type, parameter types, annotations, etc.

It's taking us a bit of work to get the metamodel just right, and that's the main thing that has been holding up the M6 release.

Annotations

Annotations are discussed here. The basic concept hasn't changed much in our initial implementation, but work is ongoing.

Null and Java interop

The way Ceylon handles null is one of the big attractions of the language. Ceylon features a typed null value. The object null is just an instance of the perfectly ordinary class Null from the point of view of the type system. But when the compiler backend transforms Ceylon code to Java bytecode or JavaScript, the class Null is eliminated and the value null is represented as a native Java or JavaScript null at the virtual machine level.

This is great for interoperation with native Java or JavaScript. However, when designing our Java interop, we had to take into account that Java's null isn't typed, and that the information about whether a Java method might return null, or whether a parameter of a Java method accepts null is "missing" and not available to the Ceylon typechecker. I think we've done the right thing here, but the approach we took has surprised a couple of people, so I guess it's well worth calling explicit attention to what we've done and why.

Consider the following useful method of java.lang.String:

 public String toUpperCase(Locale locale) { ... }

By checking the implementation of this method, I determined that:

  1. toUpperCase() never returns null, and that
  2. the argument to locale must not be null.

Unfortunately, this information isn't available to the Ceylon typechecker. So perhaps you might expect that Ceylon would take a very heavyhanded route, treating the return type of toUpperCase() as String?, and forcing me to explicitly narrow to String using an assertion:

assert (exists uppercased = javaString.toUpperCase(locale));

In fact, this is not what Ceylon does. This approach would make interoperation with Java a nightmare, resulting in code full of hundreds of useless assertions. You would need an assertion essentially every time you call a native method.

Worse, the "heavyhanded" approach would prevent me from passing the value null as an argument to a parameter of a Java method. What would it mean to "assert" that a method parameter accepts null? That's not an assertion that can be tested at runtime!

Nor would the "heavyhanded" approach in practice even protect me from NullPointerExceptions. As soon as I call native Java code, a NullPointerException can occur inside the Java code I call, and there's nothing Ceylon can do to protect me from that.

So instead, Ceylon takes a "lighthanded" approach at the boundary between Java and Ceylon, letting you write this:

String uppercased = javaString.toUpperCase(locale);

The typechecker knows this is a Java method, and that the information about null is missing, so it assumes that you know what you're doing and that toUpperCase() never returns null.

But what if you've got it wrong, and toUpperCase() does return null at runtime? We would not want that null value to propagate into Ceylon values declared to contain a not-null type!

What the Ceylon compiler does is insert a null check on the return value of toUpperCase(), and throws a NullPointerException to indicate the problem. If you ever see that NullPointerException, at runtime, you're supposed to change your code to this:

String? uppercased = javaString.toUpperCase(locale);

This is essentially the best Ceylon can do given the information available to it. The NPE is not a bug! It's not even really a "gotcha".

Still, the NullPointerException might look like a bug to novice user. (Indeed, Stef recently noticed someone describe it as a bug in a conference presentation.) So I'm asking myself how we can make this less surprising. There's a range of options, including:

  • add a better error message to the NullPointerException,
  • throw an AssertionException instead of a NullPointerException, or even, perhaps,
  • following the approach we use for JavaScript interop, require you to surround "lighthanded" code in a special construct that suppresses the "heavyhanded" typechecks, dynamic Null { ... } or something.

I lean towards the first of these options, I suppose.

Whatever we do, we need to document this better. Right now, the documentation is kinda hidden.