Blog tagged progress

Ceylon IDE performance improvements

Ceylon already has a very complete IDE, based on Eclipse, with many features, that we're very happy with. And considering how young it is, it's quite amazing that it already has all these features, but in the race to create the IDE and add all the features we needed to it, we have always pushed back any work in making it work faster. As a result, we're now to the point where the IDE is mostly complete in terms of features, but pretty slow for large projects.

Luckily, pushing back the work on performance doesn't mean we didn't have ideas for how to improve it, and in fact we have a lot of ideas, and today I'm glad to announce that in collaboration with Serli (one of the companies sponsoring Ceylon), David Festal is going to spend the next six months working on fixing all the speed and memory issues we have with the Ceylon IDE. David is already behind a lot of work that went in the Ceylon IDE and has experience with not just the Eclipse APIs and the Ceylon plugin, but also all the Ceylon sub-systems that the IDE integrates, such as the type-checker, the backends and the module system.

Here are the major points David will be working on for the IDE:

  • Opening a Ceylon project should only generate Ceylon binaries if their source has changed.
  • Load module dependencies using their binaries if it turns out to be faster than using their sources.
  • Make the backend compiler use the already parsed and typechecked AST that is available in the IDE.
  • Beef up the automated test suite.
  • Share the loaded models for the Ceylon language module and the JDK modules across every project.
  • Research performance bottlenecks in the typechecker and backend if required.
  • Research and fix memory consumption on some architectures.
  • Improve incremental build when editing module files.
  • Add visual progress reporting of the module system.
  • Improve build visual progress reporting.

We believe that with David's availability and experience, all of these issues will be obliterated and we will have an incredibly fast Ceylon IDE even for large projects, at the latest for January 2014. The good news is that David will start this performance run as early as next week, and we will release new versions of the Ceylon IDE as soon as each improvement is stable enough to ship, so we can expect faster releases really soon.

Go David!

Ceylon M5 and Ceylon IDE M5 now available!

Ceylon M5 “Nesa Pong” is now available for download, along with a simultaneous compatible release of Ceylon IDE. This is a huge release, with the following headline features:

  • a fully-reified type system with generic type arguments available at runtime,
  • direct interoperation with native JavaScript,
  • tuples,
  • several important syntax changes in response to community feedback and practical experience, and
  • a datetime module and an HTTP server.

You can download the Ceylon command line distribution here:

http://ceylon-lang.org/download

Or you can install Ceylon IDE from our Eclipse update site.

Language features

M5 is an almost-complete implementation of the Ceylon language, including the following new features compared to M4:

In addition, the language specification and documentation have been substantially revised and improved.

The following language features are not yet supported in M5:

  • the type safe metamodel
  • user-defined annotations
  • serialization

This page provides a quick introduction to the language. The draft language specification is the complete definition.

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.

Community

The Ceylon community site includes documentation, the current draft of the language specification, the roadmap, and information about getting involved.

http://ceylon-lang.org

SDK

The new platform modules are available in the shared community repository, Ceylon Herd.

https://herd.ceylon-lang.org

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, Xavier Coulon.

Ceylon HTTP server

One of the exciting new features coming to the Ceylon SDK in M5 is the HTTP server contributed by Matej Lazar. The HTTP server forms part of the module ceylon.net and is based on Undertow, a fairly new project that will serve up HTTP in future versions of JBoss.

The API is still evolving, but I can give you a taste of it with a little hello world program.

import ceylon.net.httpd { createServer, Endpoint, Response, Request }

void runServer() {
    //create a HTTP server
    value server = createServer {
        //an endpoint, on the path /hello
        Endpoint { 
            path = "/hello";
            //handle requests to this path
            service(Request request, Response response) =>
                    response.writeString("hello world");
        }
    };

    //start the server on port 8080
    server.start(8080);
}

Or, if you're the kind of person who gets their kicks out of squeezing things into one line:

import ceylon.net.httpd { createServer, Endpoint, Response, Request }

void runServer() => 
        createServer { Endpoint("/hello", 
                (Request request, Response response) =>
                        response.writeString("hello world")) }
        .start(8080);

(No, FTR, I would never write it like that.)

Oh, I almost forgot, you'll also need to import ceylon.net in your module descriptor:

module hellohttp '1' {
    import ceylon.net '0.5';
}

Of course, what you don't need is any kind of XML metadata or build script to package stuff up into deployment archives or copy archives to a deployment directory.

And it starts up in an instant, right from the commandline or IDE.

Reification, finally

Thanks to a bunch of hard work by Stef, Ceylon finally has fully reified types, that is, reified generic type arguments. You can now write stuff like this very contrived example:

"Check if the given value is an instance 
 of the given type."
Boolean check<T>(Anything o) => o is T;

"A string is a list of characters."
assert (check<List<Character>>("hello"));

"A string is a list of objects."
assert (check<List<Object>>("hello"));

"A string isn't a list of integers nor a 
 list of floats."
assert (!check<List<Integer>|List<Float>>("hello"));

This long-promised feature, promised almost two years ago, will be available in the upcoming M5 release of the language. The truth is that it's going to take a fair bit more work to do all the optimizations we plan in order to wring acceptable performance out of this stuff, but we've got a couple of clever ideas in this area, that I'm going to keep under wraps for now. ;-)

So why is reified generics important? Well, I took a brief stab at answering that question way back when, but since then we've spotted a couple of other compelling reasons. So lets take a quick look at one practical problem that is extremely difficult to solve without reified generics.

Ceylon's language module has an interface called Category

shared interface Category {
    shared formal Boolean contains(Object element);
    ...
}

A Category is an object that can "contain" other objects. Given an object, we can ask if it belongs to the category by calling contains(), or using the in operator.

Of course, collections are Categorys:

shared interface Collection<out Element>
    satisfies {Element*} &
              Category & 
              ...

Now, notice that the signature of contains() for a Collection<String> is contains(Object element), not contains(Element element). The reason for that is that we want collections to be covariant in their element type. I should be able to write the following:

Collection<String> strings = [ "hello", "world" ];
Collection<Object> objects = strings;
print(1 in objects);

Notice that on the last line, an Integer gets passed to the contains() method of a Collection<String>.

So the, without making use of reified generics, the default implementation of contains() defined by Collection would be as follows:

shared actual default Boolean contains(Object element) {
    for (elem in this) {
        if (exists elem, elem==element) {
            return true;
        }
    }
    else {
        return false;
    }
}

That's already kinda lame. If I have a Collection<String>, and contains() gets passed an Integer, we should be able to immediately return false like this:

shared actual default Boolean contains(Object element) {
    if (is Element element) {
        for (elem in this) {
            if (exists elem, elem==element) {
                return true;
            }
        }
    }
    return false;
}

But, well, whatever, that's merely a minor performance optimization, I suppose. But now consider the case of a Range. A Range is a kind of Collection whose elements are determined by two endpoints, which could potentially have thousands or millions or even more values in between! The above default implementation of contains(), inherited from Collection, would be so hideously expensive as to be actually wrong. So Range should refine the implementation of contains() as follows:

shared actual Boolean contains(Object element) {
    if (is Element element) {
        return includes(element);
    }
    else {
        return false;
    }
}

shared Boolean includes(Element element) =>
        decreasing then element<=first && element>=last
                else element>=first && element<=last;

The is Element element test requires reified generics. Without it, I can't be sure that the given value is a Comparable<Element>, so I can't compare the given value to the endpoints of the Range.

What I've shown here, for those interested in type systems, is that in a type system with declaration-site covariance, there are perfectly legitimate reasons to want to be able to test the runtime type of a value. Sure, ML and Haskell don't need to do this kind of thing, but those languages don't feature subtyping, and therefore don't have covariance. Object oriented languages are very different, so please take that into account before arguing that reified generics are unnecessary.

UPDATE: For more information about the implementation, see Stef's email here:

https://groups.google.com/forum/?hl=en&fromgroups=#!topic/ceylon-dev/mIrroqhcf7o

UPDATE 2: It was very remiss of me to not mention that we've had support for reified generics in the JavaScript backend for a while now. Thanks, Enrique!

Ceylon M5 progress report

Ceylon M4 was released back at the end of October, more than three months ago, so you might be wondering what the hell have we been up to and where in hell is that M5 release we promised! At the very least you deserve a progress report.

So what's going on is that M5 has gone quite a bit off the roadmap. This was kind of my last chance to revisit some design choices I made a couple of years ago before there was any kind of compiler for Ceylon or libraries written in Ceylon. Inevitably, there were some things that seemed like a good idea at the time, but:

  • I never really managed to sell to everyone else,
  • we ended up falling out of love with, or
  • just didn't quite work out in practice once we started writing real code.

This was really our last opportunity to fix things like that, so we've lost some time there.

Additionally, there was one thing, namely tuples, that I didn't think we were going to need, that we did wind up needing, and accommodating it elegantly into the language was actually somewhat disruptive. This also cost some time, and there is still some unfinished work there.

Finally, we decided to throw away almost all the human-compiled code in the language module and compile it with the Ceylon compiler instead. That was hard work, but it sure makes as feel a lot more secure and saves us a lot of pain when we add things to the language module.

So the bad news is that, given the above, some functionality is going to slip from M5, and therefore M5 will not be a feature-complete implementation of the language. In particular, it wont have support for:

  • annotations,
  • serialization, or
  • the metamodel (reflection).

It will have the following new features:

  • tuples
  • direct interop with native JavaScript APIs via the new dynamic block (more on this in a separate post)
  • the : operator
  • verbatim strings
  • fat arrow => abbreviation for defining single-expression functions
  • the late annotation
  • binary and hexadecimal numeric literals
  • defaulted type parameters
  • experimental support for reified generics
  • many miscellaneous minor improvements to the syntax and typing rules of the language
  • ceylon.time module
  • ceylon.net.httpd package (a HTTP server)
  • the compose() and curry() functions

(Please don't try to tell me that's not a pretty impressive list!)

I also finally found the time to give the language specification some love, after several months of neglect, so it's now up to date with all this new work. Ceylon is a better language now, I'm certain of it.

Well, I've a little more bad news: M5 is still not ready :-(

  • Stef's currently putting the finishing touches on his initial implementation of reified generics,
  • David's working on some performance issues with compilation inside the IDE that we would prefer to see fixed in M5,
  • Roland Tepp is finishing off work on the initial release of ceylon.time,
  • Matej Lazar is doing some API improvements to ceylon.net.httpd, and
  • there's still a bunch of backend bugs that need fixing.

Bear with us. It's frankly very painful for us to have not had a release in almost four months, but in a funny way that's more of a sign of how close we are now to delivering something you can really use to write real applications.