Ceylon language

bearophile bearophileHUGS at lycos.com
Wed Apr 13 05:34:22 PDT 2011


The first description of the Ceylon language, designed for business computing (for large teams developing multi-user applications) and meant to replace Java, from Red Hat, that will run on the Java Virtual Machine itself:
http://blog.talawah.net/2011/04/gavin-king-unviels-red-hats-top-secret.html

About the language (very slow download):
http://www.qconbeijing.com/download/Gavin%20keynote.pdf
About the its type system:
http://www.qconbeijing.com/download/Gavin%20session.pdf


Some of the Java things they are frustrated by:
- non-typesafety of null values
- the dangerous synchronized keyword
- clumsy annotation syntax
- verbose constructor syntax
- all Java objects are semaphores?!
- SE SDK overuses stateful (mutable) objects


The following parts are from the PDF documents, plus few comments of mine.

-------------

Ceylon does not support method overloading (or any other kind of overloading).

-------------

If a value of type T can be null, it must be declared as type Optional<T>, which may be abbreviated to T?  

String? name = process.args.first;
if (exists name) {
    writeLine("Hello " name "!");
}
else {
    writeLine("Hello World!");
}

Use of an optional value must be guarded by the if (exists ... ) construct. Therefore, NullPointerExceptions are impossible.

This is exactly what I suggested for D in a enhancement request.
It seems this kind of stuff is becoming a standard in new languages. 

-------------

Attributes and local variables are immutable by default. Assignable values must be annotated variable:

variable Natural count := 0;

-------------

A getter looks like a method without a parameter list:

shared Natural currentValue { 
        return count; 
    }
}


Attributes are polymorphic. A subclass may override a superclass attribute. It may even override a simple attribute with a getter or vice versa!

This means there is no need for explicit getter/setters until you are ready for them. This is nice.

-------------

There is no new keyword:

Counter c = Counter();

-------------


The local keyword may be used in place of a type for block-local declarations:

local c = Counter();

-------------

Assignment to a variable value or attribute setter is done using the := operator. The = specifier is used only for specifying immutable values:

shared assign currentValue {
    count := currentValue;
}

-------------

We may define a class method "by reference":

void hello(String name) = hello;

-------------

A method may declare multiple lists of parameters. The method body is executed after arguments have been supplied to all parameter lists:

Float add(Float x)(Float y) {
    return x+y;
}

This is a kind of user defined and safe partial application, it's a cute idea.

Providing arguments to just one parameter list produces a method reference:

Float addOne(Float y) = add(1.0);
Float three = addOne(2.0);

(The point of all this is that we are able to provide all the functionality of first-class and higher-order functions without needing to resort to unnatural syntactic constructs inspired by the lambda calculus notation.)

-------------

There is a Named argument syntax. They use it for a syntax trick: A named argument invocation is enclosed in braces, and non-vararg arguments are listed using the name=value; syntax.

This seems one of the most peculiar and refined parts of the syntax of this language. See slides 34-37 in the first PDF.

-------------

A class or interface satisfies zero or more interfaces

shared class Character(Natural utf16) 
    extends Object()
    satisfies Ordinal & Comparable<Character> {
        ...
}

The syntax X&Y represents the intersection of two types. The syntax X|Y represents the union of two types.

-------------

The "actual" annotation specifies that a member refines a supertype member:

shared class Character(Natural utf16) 
        extends Object()
        satisfies Ordinal & Comparable<Character> {
        
    Natural nat = utf16;

    shared actual Comparison compare(T that) {
        return this.nat <=> that.nat;
    }


The <=> operator is called "compare". It's just a shortcut for the method compare() of Comparable.

-------------

Type narrowing, "Switching" by type

Type narrowing is often frowned upon in object-oriented programming

Unfortunately, Java exacerbates the problem:
- the compiler does not inform us when addition of a new subtype breaks the list of cases


Node<String> node = ... ;
switch (node)
case (is Leaf<String>) { 
    leaf(node.value); 
}
case (is Branch<String>) { 
    branch(node.left, node.right); 
}
else {
    somethingElse(node);
}

The compiler forces the switch statement to contain an else clause to handle other subtypes.

-------------

Enumerated subtypes

A class or interface may specify an explicitly enumerated list of subtypes. The functional programming community calls this an algebraic datatype:

abstract class Node<T>(String name) 
       of Branch<T> | Leaf<T> { ... }

lass Leaf<T>(String name, T value) 
       extends Node<T>(name) { ... }

lass Branch<T>(String name, Node<T> left, Node<T> right
       extends Node<T>(name) { ... }


The compiler validates that switch statements contain either an exhaustive list of possible subtypes or an else clause:

Node<String> node = ... ;
switch (node)
case (is Leaf<String>) { 
    leaf(node.value); 
}
case (is Branch<String>) { 
    branch(node.left, node.right); 
}

-------------

A type may be covariant or contravariant in its type parameter. (Respectively in or out.):

interface WeakReferenceGetter<out T> { 
    shared formal T? get(T t); // Compile error: not covariant
}
interface WeakReferenceSetter<in T> { 
    shared formal T set(T t); // Compile error: not contravariant
}

The compiler validates member signatures to check that the type really does respect the declared variance. This is way easier to understand than wildcard types in Java.


Collections and variance:
- An interface like List<T> should be covariant in T since we almost always want a List<String> to be a List<Object>
- Therefore, we need to split operations which mutate the list to a separate interface OpenList<T>

-------------

Bye,
bearophile


More information about the Digitalmars-d mailing list