Note: information on this page refers to Ceylon 1.0, not to the
current release.

The language module

This is the fourteenth part of the Tour of Ceylon. The previous part
introduced comprehensions. We're now going to learn about Ceylon's
language module and some of the basic types it defines.

The language module is special, because it is referred to by the language
specification, and some language-level constructs are defined in terms of
the types it declares. Therefore, you can think of it as forming part of
the language definition. In practice, the language module is implemented
in a mix of Ceylon and native (Java and JavaScript) code. Of course, we've
already met quite a few of the inhabitants of the language module,
especially in this chapter.

An overview of the language module

The module ceylon.language
contains classes and interfaces that are referred to in the language
specification, other declarations they refer to, and a number of related
useful functions and types. Let's meet the main characters.

In Ceylon, Objectisn't the root of the type system. An expression of
type Object has a definite, well-defined, non-null value.
As we've seen, the Ceylon type system can also represent some more exotic
types, for example
Null,
which is the type of null.

"The abstract supertype of all types. A value of type
`Anything` may be a definite value of type `Object`, or it
may be the `null` value. A method declared `void` is
considered to have the return type `Anything`..."
shared abstract class Anything()
of Object | Null {}

All Ceylon types are assignable to Anything. Expressions of type Anything
aren't useful for very much, since Anything has no members or operations.
The one useful thing you can do with Anything is represent the signature of
a method when you don't care about the return type, since a method declared
void is considered to have return type Anything, as we saw in the
part about functions.

The class Null also directly extends Anything.

"The type of the `null` value. Any union type of form
`Null|T` is considered an optional type, whose values
include `null`. Any type of this form may be written as
`T?` for convenience..."
shared abstract class Null()
of null
extends Anything() {}

Since an expression of type Object always evaluates to a definite,
well-defined value, it's possible to obtain the runtime type of an
Object, or narrow an expression of type Object to a more specific
type.

Equality and identity

On the other hand, since Object is a supertype of types like Float
which are passed by value at the level of the virtual machine, you
can't use the === operator to test the identity of two values of type
Object. The following is not allowed:

assert (x===1); //compile error: Integer is not Identifiable

Instead, === is defined to act on instances of the interface
Identifiable.
Integer, Float, Character, and Stringdon't satisfy this
interface, but most classes do.

"The abstract supertype of all types with a well-defined
notion of identity. Values of type `Identifiable` may
be compared using the `===` operator to determine if
they are references to the same object instance..."
shared interface Identifiable {
"Identity equality comparing the identity of the two
values..."
shared default actual Boolean equals(Object that) {
if (is Identifiable that) {
return this===that;
}
else {
return false;
}
}
"The system-defined identity hash value of the
instance..."
see (`function identityHash`)
shared default actual Integer hash => identityHash(this);
}

Identifiable implements the hash attribute and equals() method of
Object, which are very similar to the equals() and hashCode() methods
defined by java.lang.Object.

Just like in Java, you can refine this default implementation in your own
classes. This is the normal way to get a customized behavior for the ==
operator, the only constraint being, that for subtypes of Identifiable,
x===y should imply x==y— equality should be consistent with identity.

By default, a user-written class extends the class
Basic,
which extends Object and satisfies Identifiable. It's possible for a
user-written class to directly extend Object, but most of the classes
you write will be subclasses of Basic. All classes with variable
attributes must extend Basic.

Operator polymorphism

Ceylon discourages the creation of intriguing executable ASCII art.
Therefore, true operator overloading is not supported by the language.
Instead, almost every operator (every one except the primitive ., (),
is, =, ===, and of operators) is considered a shortcut way of
writing some more complex expression involving other operators and ordinary
function calls.

For example, the < operator is defined in terms of the interface
Comparable,
which has a method named compare(). The operator expression

x<y

means, by definition,

x.compare(y) === smaller

The equality operator == is defined in terms of the class Object,
which has a method named equals(). So

x==y

means, by definition,

x.equals(y)

Therefore, it's easy to customize operators like < and == with specific
behavior for our own classes, just by implementing or refining methods like
compare() and equals(). Thus, we say that operators are polymorphic
in Ceylon.

Apart from Comparable and Object, which provide the underlying
definition of comparison and equality operators, the following interfaces are
also important in the definition of Ceylon's polymorphic operators:

Two < or <= operators may be combined to determine if a value falls withing
a range:

assert(0<quantity<=100);

Set operators

The operators | and & represent set union and intersection when they appear in a
value expression. But, as we've already seen, when they appear in a type expression
they represent type union and intersection! Indeed, there is a relationship between
the two kinds of union/intersection:

An instance of Character represents a 32-bit Unicode character, not a
Java-style UTF-16 char. However, under the covers, Ceylon strings are
implemented using a Java char[] array (in fact, they are implemented
using a Java string). So some operations on Ceylon strings are much
slower than you might expect, since they must take four-byte characters
into account. This includes size and item(). We think it's much better
that these operations be slow, like in Ceylon, than that they sometimes
give the wrong answer, like in Java. And
remember, it's
never correct to iterate a list using size and item() in Ceylon!

To avoid the cost of calling size(), try to use the more efficient
empty, longerThan() and shorterThan() when the string might be
very long.

Numeric types

As we've mentioned several times before, Ceylon doesn't have anything like
Java's primitive types. The types that represent numeric values are just
ordinary classes. Ceylon has fewer built-in numeric types than other C-like
languages:

However, the compiler magically eliminates these classes, wherever possible,
in order to take advantage of the high performance of the platform's native
primitive types.

Therefore, the precision of these types depends on whether you're running
your code on the JVM or on a JavaScript virtual machine.

When compiling for Java both types have 64-bit precision by default.
Eventually, you'll be able to specify that a value has 32-bit precision
by annotating it small. But note that this annotation is really just a
hint that the compiler is free to ignore (as it currently does).

When compiling for JavaScript, Floats have 64-bit precision and
Integers have 53-bit precision.

A hexadecimal integer is written using a prefix #. Digits may be grouped
into groups of two or four digits.

Integer white = #FF_FF_FF;

A binary integer is written with a prefix $. Digits may be grouped into
groups of four digits.

Integer sixtyNine = $0100_0101;

Whole and Decimal

The platform module ceylon.math defines the types Whole and Decimal,
which represent arbitrary precision integers and arbitrary precision decimals.
Both classes are subtypes of Numeric, so you can use all the usual numeric
operators with them:

Decimal num = ... ;
Decimal denom = ... ;
Decimal ratio = num/denom;

Abstracting over numeric types

Since all numeric types are subtypes of Numeric, it's possible to write
generic code that treats numeric values polymorphically.

You can pass Floats, Integers, Wholes, Decimals or any other numeric
type to ratio(). But since polymorphic numeric functions can't be optimized
to use VM-level primitive types, this function is likely to be much slower
than a function that accepts two Floats or two Integers.

Numeric widening

As mentioned earlier, Ceylon doesn't have implicit type conversions, not even
built-in conversions for numeric types. Assignment does not automatically
widen (or narrow) numeric values. Instead, we usually need to call one of the
operations (well, attributes, actually) defined by the interface
Number.

Float zero = 0.float; // explicitly widen from Integer

You can use all the operators you're used to from other C-style languages
with the numeric types. You can also use the ^ operator to raise a
number to a power:

Float diagonal = (length^2.0+width^2.0)^0.5;

Of course, if you want to use the increment ++ operator, decrement --
operator, or one of the compound assignment operators such as +=, you'll
have to declare the value variable.

Since it's quite noisy to explicitly perform numeric widening in numeric
expressions, the numeric operators do automatically widen their operands,
so we could write the expression above like this:

Float diagonal = (length^2+width^2)^(1.0/2);

Since ceylon.language only has two numeric types the only automatic
widening conversion is from Integer to Float. This is the one and
only thing approaching an implicit type conversion in the whole language.

Collections

The language module includes several interfaces that represent container
types:
Collection,
List,
Map, and
Set.

You might be disappointed to discover that there are no general-purpose
implementations of these interfaces in the language module itself. In fact,
they're only declared here so that String, Sequence, and
Array
can be subtypes of List.

You might be even more disappointed when you look at these interfaces and
discover that they're missing half the useful operations you're used to
seeing on a collection: they have no operations at all for building or
mutating the collection. Actually, there's a couple of good reasons for this:

It's usually best for an API to return an obviously read-only collection
to clients, instead of leaving the client scratching his head wondering
whether mutating this collection results in mutation of the internal data
structures held by the API, and whether this is safe.

Making these interfaces read-only means they can be declared
covariant in their type
parameters.

The module ceylon.collection contains general-purpose implementations of
these interfaces, along with APIs for building and mutating collectons.

There's more...

The language module isn't by itself a platform for building applications.
It's a minimal set of basic types that form part of the language definition
itself. The Ceylon SDK is still under development, but you can already
start using ceylon.file,
ceylon.process, and
ceylon.math.

Next we're going to come back to the subject of object initialization,
and deal with a subtle problem affecting languages like Java and C#.