Can sealed enforcement work across module boundaries?
← Type Hierarchy Constraints · Ref: Q609
Yes, sealed enforcement (E05240) works across module boundaries. The 'allow only' list names specific concrete types, and the compiler checks every concrete class that attempts to extend or implement the sealed type, regardless of which module it is in.
TRANSITIVE CHECKING
The check is transitive. If Module A defines a sealed trait and Module B defines a class implementing it, the compiler still verifies that Module B's class is in the 'allow only' list. If not, E05240 fires.
WHY CROSS-MODULE WORKS
EK9 compiles all referenced modules together. The full type hierarchy is available during TYPE_HIERARCHY_CHECKS, so the compiler has complete visibility across module boundaries.
LIMITATIONS
The permitted types must be resolvable from the sealed type's module. This means they must be in the same module or in a module that is a dependency. You cannot list a type from a module that the sealed type's module doesn't know about.
ABSTRACT INTERMEDIARIES
Abstract classes can implement a sealed trait from any module without being in the 'allow only' list. Only concrete subclasses need to be listed.
See Q298 for sealed trait basics. See Q300 for abstract exemptions. See Q607 for sealed class requirements. See Q6 for module organization.
Example
defines module qa.typehierarchy.crossmodule defines trait //Sealed trait with all permitted types defined in same module //In practice, permitted types could be in different modules Serializable allow only JsonFormat, XmlFormat, CsvFormat serialize() as abstract <- rtn as String? defines class JsonFormat with trait of Serializable override serialize() <- rtn as String: "json" XmlFormat with trait of Serializable override serialize() <- rtn as String: "xml" CsvFormat with trait of Serializable override serialize() <- rtn as String: "csv" defines function testSealedCrossModule() formats <- List() of Serializable formats += JsonFormat() formats += XmlFormat() formats += CsvFormat() for fmt in formats result <- fmt.serialize() require result?
Common mistakes
E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.
Incorrect:
result <- fmt.serialize().toUpperCase()
Correct:
result <- fmt.serialize()
E05120 — When implementing the abstract serialize method from the sealed Serializable trait, the override keyword is required. Omitting it triggers shadowing detection. See ek9 -h E05120 for details.
Incorrect:
serialize()
Correct:
override serialize()
Other ways to ask this
- Does allow only work across EK9 modules?
- How does EK9 enforce sealed types in different modules?
- Are sealed constraints checked across packages?
Coming from another language?
Java: sealed interfaces work across packages but within the same module. Kotlin: sealed hierarchy restricted to same package/module. Rust: orphan rule restricts trait implementation to same crate. EK9: sealed enforcement works across modules with full transitive checking.
Keywords: cross, hierarchy, exhaustive, enforcement, sealed, boundary, type, package, circular, module, visibility, E05240, closed, transitive, inherit