Ceylon Union, Intersection, and Optional Types: A Decision Guide for 2026
Ceylon's union, intersection and optional types model alternatives and absence without wrappers, with compile-time narrowing. This guide compares the constructs, shows a narrowing example, and weighs the dormant 1.3.3 maintenance status for 2026 use.
19 May 2026, 12:43 UTC

The Decision: Should You Use Ceylon's Type System for New Work?
Ceylon's union types (A|B), intersection types (A&B), and optional-type sugar (T? meaning T|Nothing) let you model alternatives and absence directly in signatures—no wrapper classes, no sentinel values, no unchecked null. The compiler forces callers to narrow before use, turning potential NullPointerExceptions into compile errors.
The catch: the last stable release was Ceylon 1.3.3 in 2017. Active development stopped after the Eclipse Foundation transition. Adopting Ceylon today is a legacy or niche choice. If you are maintaining an existing Ceylon codebase, these features are already yours. If you are starting fresh on the JVM, you are weighing a dormant language against Kotlin, Scala 3, or Java 21+ with pattern matching.
Constraints That Shape the Decision
- Toolchain viability: Verify the 1.3.3 distribution still runs on your target JDK (tested up to JDK 17 in community reports; JDK 21+ is uncharted).
- Team familiarity: Java-trained developers need to learn narrowing ceremonies (
if (exists x), exhaustiveswitch). - Java interop: Java platform types are treated conservatively; nullability annotations are not automatically trusted. Pin examples to a specific compiler version.
- Ecosystem: Ceylon Herd (the module repository) is read-only. No new libraries, no security patches.
Supported Options at a Glance
| Notation | Meaning | Typical Use | Narrowing Required |
|---|---|---|---|
T? | Optional: T|Nothing | Return value or absence (e.g., lookup, parse) | if (exists v) or assert (exists v) |
A|B | Union: one of several types | Multiple result shapes (String|Integer), error-or-value | Exhaustive switch with case (is A) … |
A&B | Intersection: must satisfy both | Capability composition (Serializable&Comparable) | None; value already has both types |
alias Token => String|Integer | Named union alias | Recurring union in public API | Same as underlying union |
given Value of A|B|C | Type-argument constraint | Restrict generics to an enumerated set | Constraint checked at declaration site |
Trade-offs
What You Gain
- Honest APIs: A method returning
User|NotFounddocuments its contract in the signature. - No wrapper boilerplate: No
Optional<T>, noEither<L,R>, no sentinel-1. - Compile-time safety: The compiler rejects un-narrowed uses and incomplete switches.
- Intersection composition: A parameter typed
Closeable&Flushableaccepts only objects implementing both—no marker interface needed.
What You Pay
- Narrowing ceremony: Every use of a
T?or union value requires anif (exists ...)block or exhaustiveswitch. Call sites become verbose. - Learning curve: Intersection types and union exhaustiveness are unfamiliar to most JVM developers.
- Java interop friction: Java's
StringbecomesString?in Ceylon; you must narrow even when you \"know\" it's non-null. - No future fixes: A JDK change that breaks the compiler or runtime will not be patched upstream.
Concrete Implementation: Parsing with Optional Return
The following snippet demonstrates the core workflow: declare an optional return, call it, and narrow with if (exists ...). The compiler rejects any use of n outside the exists block.
// Ceylon 1.3.3 syntax (verify against the 1.3.x spec)
shared Integer? parse(String text) {
if (text.matches(\"\\\\d+\")) {
return Integer.parse(text);
}
return null; // null is the sole instance of Nothing
}
shared void run() {
value input = \"42\";
value n = parse(input);
// n has type Integer? here — cannot use as Integer
if (exists n) {
// n narrowed to Integer inside this block
print(n + 1); // prints 43
}
// n is Integer? again here
}
To validate locally:
- Install the Ceylon 1.3.3 distribution (
ceylon-1.3.3.zipfrom the Eclipse Ceylon GitHub releases). - Run
ceylon --versionto confirm the toolchain starts. - Save the snippet as
source/com/example/parse.ceylon. - Compile:
ceylon compile com.example(run from the directory containingsource/). - Execute:
ceylon run com.example. - Intentionally break narrowing—remove the
if (exists n)and writeprint(n + 1);directly. The compiler must error with \"value of type Integer? cannot be assigned to Integer\".
Expected check: The clean compile succeeds and runs; the broken version fails at compile time. No runtime NullPointerException is possible for the narrowed variable.
Union Exhaustiveness Example
alias Result => String|Integer|Error;
shared String describe(Result r) {
switch (r)
case (is String) { return \"string: \" + r; }
case (is Integer) { return \"int: \" + r.string; }
case (is Error) { return \"error: \" + r.message; }
// no default — compiler verifies all cases covered
}
Add a fourth type to Result without updating describe and the compiler will flag the incomplete switch.
Intersection in Practice
shared void save(Serializable&Closeable resource) {
try {
resource.close(); // both methods available
} catch (Exception e) { ... }
}
Callers must pass an object implementing both interfaces. No adapter class required.
Verification Checklist Before Committing
- Run the parse example on your target JDK. Confirm
ceylon compileandceylon runsucceed. - Confirm the compiler rejects un-narrowed
T?use and incomplete union switches. - Check the Ceylon GitHub organization (
github.com/eclipse/ceylon) for any commits after 2017; none means truly dormant. - Read the Ceylon 1.3.x language specification sections on Union types, Intersection types, Optional types, and Exists narrowing to lock down exact syntax.
- Test a Java interop call: pass a Java
Stringto a Ceylon function expectingString?and verify you must narrow.
Limitations
- No security updates for the compiler or runtime.
- No IDE support in current IntelliJ/Eclipse versions (the Ceylon IDE plugins are unmaintained).
- Build-tool integration (Maven/Gradle plugins) is stale; you may need to invoke
ceylonCLI directly. - Documentation links in the official site may redirect to archived pages.
Bottom Line
If you are extending a Ceylon 1.3.x codebase, lean into union, intersection, and optional types—they are the language's strongest feature and the compiler enforces their correct use. For any new project in 2026, the maintenance risk outweighs the type-system benefits. Choose a language with active releases and a living ecosystem.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.