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