Type aliases help us reduce verbosity, because instead of repeatedly writing
out the same generic type, for example Set<Person>, we can use a snappier
alias, such as People. But in some cases, as we're about to see below,
Ceylon lets us omit the type altogether.

A toplevel type alias or a type alias belonging to a class or interface may
be shared.

shared interface People => Set<Person>;

Member class aliases and class alias refinement

When it comes to class aliases, Ceylon has one more trick up its sleeves.
Cast your mind back to what we learned about member classes in the
fifth leg of the tour.
What we saw there with ordinary classes also applies to class aliases.

A type alias may be nested inside a class or interface. In the case of a
class alias, it is considered a member of the class or interface:

(Alternatively, we could have written extends MutableList<Character>()
instead of extends super.Buffer(), since both expression refer to the
same class type.)

Type inference

So far, we've always been explicitly specifying the type of every declaration.
This generally makes code, especially example code, much easier to read and
understand.

However, Ceylon does have the ability to infer the type of a local variable
or the return type of a local method. Just place the keyword value (in the
case of a local value) or function (in the case of a local function) in
place of the type declaration.

(Note, this last restriction will be removed in a future version of the
language!)

"Left to right" type inference

These restrictions mean that Ceylon's type inference rules are quite simple.
Type inference is purely "right-to-left" and "top-to-bottom". The type of any
expression is already known without needing to look to any types declared
to the left of the = specifier, or further down the block of statements.

The inferred type of a reference declared value is just the type of the
expression assigned to it using =.

The inferred type of a getter declared value is just the union of the
returned expression types appearing in the getter's return statements
(or Nothing if the getter has no return statement).

The inferred type of a method declared function is just the union of the
returned expression types appearing in the method's return statements
(or Nothing if the method has no return statement).

{X+} where X is the common superclass or super-interface
of all the element types.

But that can't be right, since there might be more than one common
supertype, and supertypes have no well-defined linearization (order).

The correct answer is that the inferred type is {X+} where X is the
union of all the element expression types. In this case, the type is
{Polar|Cartesian+}.

Now, since Iterable<T> is
covariant in T, and since
a union type is the most precise common supertype of any two types, it
turns out that coords is of type {X+}for everyX in the set of
common supertypes of the element types. That this even works out is due
to a property of Ceylon's type system called principal typing.

The type of the attribute first of Iterable<Element> is Element?.
Here, we have an Iterable<String?>. Substituting String? for Element,
we get the type String??, that is, Null|Null|String, which is simply
Null|String, written String?. Of course, since the compiler can figure
out that kind of thing for us, we could have simply written:

It's interesting just how useful union types turn out to be. Even if you only
rarely write code with explicit union type declarations, they're still there,
under the covers, helping the compiler solve some hairy, otherwise-ambiguous,
typing problems.

Note that what we've just seen is really just a special case of the algorithm
Ceylon uses for generic type argument inference, and all of the above works
just as well for user-written generic types as it does for
Iterable.

Gotcha!

Very occasionally, this "collapsing" behavior of unions of Nulls—that
Null|Null|T is just Null|T—is inconvenient. Imagine that we wanted
to have a Map that distinguished between:

a key for which the map has no entry, and

a key for which the map has an entry with a no item.

We might try to use a Map<String,Item?> for this. But then map.get(key)
would simply return null in both of the above cases, so how could we
distinguish between them? One way would be to call map.defines(key), but
that would result in an additional lookup.

In practice, what we actually do is call
Map.getOrDefault(),
but, for the sake of argument, let's pretend that getOrDefault() isn't there,
and ask how else we could solve the problem.

There's two idioms that we could use to handle this situation. The first uses
a "wrapper" object for each entry: