Ceylon has several different concepts for things that might all be considered some kind of array: List
, Tuple
, Sequence
, Sequential
, Iterable
, Array
, Collection
, Category
, etc. What's is different about these these types and when should I use them?

- 8,331
- 8
- 53
- 82
2 Answers
The best place to start to learn about these things at a basic level is the Ceylon tour. And the place to learn about these things in depth is the module API. It can also be helpful to look at the source files for these.
Like all good modern programming languages, the first few interfaces are super abstract. They are built around one formal member and provide their functionality through a bunch of default and actual members. (In programming languages created before Java 8, you may have heard these called "traits" to distinguish them from traditional interfaces which have only formal members and no functionality.)
Category
Let's start by talking about the interface Category
. It represents types of which you can ask "does this collection contain this object", but you may not necessarily be able to get any of the members out of the collection. It's formal member is:
shared formal Boolean contains(Element element)
An example might be the set of all the factors of a large number—you can efficiently test if any integer is a factor, but not efficiently get all the factors.
Iterable
A subtype of Category
is the interface Iterable
. It represents types from which you can get each element one at a time, but not necessarily index the elements. The elements may not have a well-defined order. The elements may not even exist yet but are generated on the fly. The collection may even be infinitely long! It's formal member is:
shared formal Iterator<Element> iterator()
An example would be a stream of characters like standard out. Another example would be a range of integers provided to a for loop, for which it is more memory efficient to generate the numbers one at a time.
This is a special type in Ceylon and can be abbreviated {Element*}
or {Element+}
depending on if the iterable might be empty or is definitely not empty, respectively.
Collection
One of Iterable
's subtypes is the interface Collection
. It has one formal member:
shared formal Collection<Element> clone()
But that doesn't really matter. The important thing that defines a Collection
is this line in the documentation:
All
Collection
s are required to support a well-defined notion of value equality, but the definition of equality depends upon the kind of collection.
Basically, a Collection
is a collection who structure is well-defined enough to be equatable to each other and clonable. This requirement for a well-defined structure means that this is the last of the super abstract interfaces, and the rest are going to look like more familiar collections.
List
One of Collection
's subtypes is the interface List
. It represents a collection whose elements we can get by index (myList[42]
). Use this type when your function requires an array to get things out of, but doesn't care if it is mutable or immutable. It has a few formal methods, but the important one comes from its other supertype Correspondence
:
shared formal Item? get(Integer key)
Sequential, Sequence, Empty
The most important of List
's subtypes is the interface Sequential
. It represents an immutable List
. Ceylon loves this type and builds a lot of syntax around it. It is known as [Element*]
and Element[]
. It has exactly two subtypes:
Empty
(aka[]
), which represents empty collectionsSequence
(aka[Element+]
), which represents nonempty collections.
Because the collections are immutable, there are lots of things you can do with them that you can't do with mutable collections. For one, numerous operations can fail with null
on empty lists, like reduce
and first
, but if you first test that the type is Sequence
then you can guarantee these operations will always succeed because the collection can't become empty later (they're immutable after all).
Tuple
A very special subtype of Sequence
is Tuple
, the first true class listed here. Unlike Sequence
, where all the elements are constrained to one type Element
, a Tuple
has a type for each element. It gets special syntax in Ceylon, where [String, Integer, String]
is an immutable list of exactly three elements with exactly those types in exactly that order.
Array
Another subtype of List
is Array
, also a true class. This is the familiar Java array, a mutable fixed-size list of elements.

- 8,331
- 8
- 53
- 82
drhagen has already answered the first part of your question very well, so I’m just going to say a bit on the second part: when do you use which type?
In general: when writing a function, make it accept the most general type that supports the operations you need. So far, so obvious.
Category
is very abstract and rarely useful.
Iterable
should be used if you expect some stream of elements which you’re just going to iterate over (or use stream operations like filter
, map
, etc.).
Another thing to consider about Iterable
is that it has some extra syntax sugar in named arguments:
void printAll({Anything*} things, String prefix = "") {
for (thing in things) {
print(prefix + (thing?.string else "<null>"));
}
}
printAll { "a", "b", "c" };
printAll { prefix = "X"; "a", "b", "c" };
Any parameter of type Iterable
can be supplied as a list of comma-separated arguments at the end of a named argument list. That is,
printAll { "a", "b", "c" };
is equivalent to
printAll { things = { "a", "b", "c" }; };
This allows you to craft DSL-style expressions; the tour has some nice examples.
Collection
is, like Correspondence
, fairly abstract and in my experience rarely used directly.
List
sounds like it should be a often used type, but actually I don’t recall using it a lot. I’m not sure why. I seem to skip over it and declare my parameters as either Iterable
or Sequential
.
Sequential
and Sequence
are when you want an immutable, fixed-length list. It also has some syntax sugar: variadic methods like void foo(String* bar)
are a shortcut for a Sequential
or Sequence
parameter. Sequential
also allows you to do use the nonempty
operator, which often works out nicely in combination with first
and rest
:
String commaSeparated(String[] strings) {
if (nonempty strings) {
value sb = StringBuilder();
sb.append(strings.first); // known to exist
for (string in strings.rest) { // skip the first one
sb.append(", ").append(string);
// we don’t need a separate boolean to track if we need the comma or not :)
}
return sb.string;
} else {
return "";
}
}
I usually use Sequential
and Sequence
when I’m going to iterate over a stream several times (which could be expensive for a generic Iterable
), though List
might be the better interface for that.
Tuple
should never be used as Tuple
(except in the rare case where you’re abstracting over them), but with the [X, Y, Z]
syntax sugar it’s often useful. You can often refine a Sequential
member to a Tuple
in a subclass, e. g. the superclass has a <String|Integer>[] elements
which in one subclass is known to be a [String, Integer] elements
.
Array
I’ve never used as a parameter type, only rarely as a class to instantiate and use.

- 2,584
- 1
- 17
- 31
-
I don't understand how your `printAll` example can even work. You write that "Any parameter of type Iterable can be supplied as a list of comma-separated arguments at the end of a named argument list". But how can this be possible when calling `printAll`, seeing that the named argument `prefix ` comes AFTER the named argument `strings` in the function definition. – Aug 03 '18 at 16:54
-
Well, `prefix` is optional, so you’re allowed to omit it. And a comma-separated argument list in a named argument list always applies to the first unassigned `Iterable` argument, which is `things`. The order of `things` and `prefix` doesn’t matter directly in this case (though it’s still not possible to swap the parameters for a different reason – you can’t have a required parameter (`things`) after an optional one (`prefix`)). – Lucas Werkmeister Aug 04 '18 at 18:01
-
You use the concept 'a named argument list', but how does Ceylon know that we're dealing wth 'a named argument list' in this case: `printAll { "a", "b", "c" };` – Aug 05 '18 at 13:53
-
Or do you refer to characteristics of function definitions when you talk about 'named argument lists'? Then my question will be almost the same: How does Ceylon know that the argument list in the following function definition is particularly 'namish', compared to the argument lists of other function definitions? `void printAll({Anything*} things, String prefix = "") { for (thing in things) { print(prefix + (thing?.string else "
")); } }` – Aug 05 '18 at 13:58 -
1A named argument list is one enclosed in braces (`f { argument; }`), a positional argument list is one enclosed in parentheses (`f(argument)`). You can call any¹ function with either syntax, it’s up to you. (¹at least in pure Ceylon; interop with other languages can get finicky) – Lucas Werkmeister Aug 06 '18 at 20:28