Team blog

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!

Expressiveness

I just noticed a tweet by Paul Snively that I thought was interesting. I don't make it my practice to respond to tweets, simply because >99.9% of them are just idiotic. But I'll make an exception here, because I respect Paul, because he frames an interesting issue in a way I agree with, and because his tweet, which I ultimately disagree with, as I'll explain, is certainly at least partly true, at least from a certain perspective. Paul wrote:

One reason I prefer #scala to #kotlin or #ceylon is it adheres to the #lisp #smalltalk dictum of not confining expressive power to itself.

So, let's start by seeing what's right about this. Where in the language definition does Ceylon "reserve expressive power to itself"?

Well, the things that stand out to me are:

  • control structures,
  • operators,
  • type name abbreviations.

We "reserve expressive power" here, in the sense that we don't let you write your own MyTuple class, and then instantiate it using this syntax:

[String,String] myTuple = ["hello","world"];

If you want to use the brackets, you're stuck with our Tuple class, and if it doesn't suit your needs just right, you're going to have to write:

MyTuple<String,MyTuple<String>> myTuple = MyTuple("hello",MyTuple("world"));

Or whatever. Likewise, while you can certainly use the + operator with your own Complex class, if you want to use it to add a Period to a Datetime, you're out of luck. The Summable<Other> interface expresses that + must be a symmetric operation via the self-type constraint on Other. You're going to have to write:

value date = delay after now;

Or whatever.

Now, I could write a long post defending this choice, but here I'll simply note that:

  • Scala doesn't let you define type abbreviations or control structures either, but
  • it does let you define your own operators.

That is, Scala, like plenty of other languages, lets you take any arbitrary string of unicode characters and turn it into an operator.

And just look at what a mess that turned out to be. Sure, there are some nice uses of operator overloading in the Scala world (parser combinators), along with some abominations (SBT). On balance, I, and many others, believe that untrammeled operator overloading has been a net negative in Scala and C++ and other languages that have tried it. So Ceylon takes a middle road: operator polymorphism.

This means that you're stuck with the operators we think are important. But at least everyone in the Ceylon community is stuck with the same ones! Which makes Ceylon code much more immediately understandable to someone who knows the syntax of Ceylon, because the things which aren't predefined symbolic operators have meaningful names. So if by "expressive power", you take into account the ability to be understood by others, I think of this as a feature.

Fine, anyway, we can agree to disagree on this one, it's not the main point I want to make.

The real point I want to make is that I measure "expressive power" of a language, not mainly by what superficial syntax it supports (though syntax is certainly not unimportant), but rather by what can be expressed within the type system. And here's where it has been a central animating design principle that we weren't going to build in special little escape hatches or ad hoc features for things we think are important. That's why, for example, null is an instance of the ordinary class Null in Ceylon, unlike in Java or Scala where it's a primitively defined value of the bottom type. That's why Tuple is just an ordinary class, and Callable is just an ordinary interface, unlike in many languages where tuple types and function types are defined primitively. That's why the operators we do have are mostly defined to operate against generic interfaces instead of against an enumerated list of language module types.

Now, sure, of course Ceylon could never be compared to Lisp, or even to Smalltalk in this respect. But that's simply an unfair comparison. Of course a dynamically typed language is more expressive than a language with static typing. Duh. You can't reasonably compare the expressiveness of ML or Haskell to Lisp either. But statically typed languages have their own advantages, and that's why static typing is where the real action is right now.

So I think it's a misunderstanding of Ceylon to imagine that we're reserving much expressive power to oursevles. No, we don't let you define your own pope operator (-:|-+>, and I guess some people will find that limits their self-expression. But I believe Ceylon will foster other productive avenues for them to express their creativity.

UPDATE:

To take this argument even further, consider our rule against the use of non-denoteable types: we don't use non-denotable types, or operations upon types that can't be expressed within the language itself, even internally in the typechecker to reason about your code. Since we needed union and intersection types to reason about generics, we needed to make them a part of the language. And they turned out to be perhaps the most interesting and powerful feature of our typesystem.

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.

Things static type systems shouldn't have

If you've worked with any programming language, you have gripes about it. All design involves compromises, and all languages have warts. More interesting are the warts that are shared by many languages. Here's a short list of things that are extremely common in static type systems, that I think are almost always mistakes.

A populated bottom type

By definition, the bottom type is a type with no values. If null is an instance of the bottom type, that's a hole in the type system. Yes, yes, it's done for convenience. But if we were to go round adding holes into our type system every time we run into something slightly inconvenient, we may as well just give up and go use a dynamic language.

Non-denoteable types

A non-denoteable type is a type which can't be written down in the programming language itself. Many languages with sophisticated static type systems require the use of non-denoteable types to typecheck the code. This almost guarantees that the programmer can't always reproduce the compiler's reasoning, or understand the resulting error messages.

Aggressively replacing unions with intersections

For some reason, there's some kind of prejudice against union types. Your typical compiler, upon encountering something internally that is naturally a union type, immediately attempts to replace it with the intersection of its supertypes. This doesn't make a whole lot of sense to me, since in these languages both unions and intersections are usually equally non-denoteable, and this operation involves throwing away a whole lot of useful information about the type.

Assigning references a single immutable type

A value has an immutable type. A reference to a value doesn't need to. Indeed, what the compiler knows (or rather could know) about the type of a reference changes depending upon precisely where we are in the code. But there's a strong tradition that each named reference has a single declared or inferred type everywhere it is used. This tradition even infects Ceylon to some extent (though this will change in future versions).

Primitive types or primitively-defined compound types

I hesitated to put this one on the list. There is an approach to building a static type system where we start with certain primitive values, layer tuple support over those types, layer records over tuples, and eventually arrive at generic abstract data types. I'm not talking about that kind of type system. What I'm talking about is where we start with a totally general way of defining abstract data types, and then stick some extra primitive types into the system because we find our supposedly totally general thing to be inadequate.

Overloading and implicit type conversions

Yes, yes, I know they seem convenient. But in fact the benefits provided by overloading or implicit type conversions are almost entirely trivial. They just don't increase the expressiveness of your type system. And they really screw up type inference, which is why you don't see these things in academic or research languages. Dynamic languages don't have anything like overloading or implicit type conversions, and nobody misses them. Because you don't need 'em. They're an abuse of the static type information.