How do I constrain generic type parameters in EK9?
← Generics · Ref: Q195
EK9 uses 'constrain by' to restrict generic type parameters to subtypes of a specific class. This enables safe access to methods of the constraining type.
CONSTRAIN BY SYNTAX
Restrict T to subtypes of a base type:
Handler of type T constrain by Animal
Now T is guaranteed to have all Animal methods.
ACCESSING CONSTRAINED METHODS
Inside the generic, T's methods from Animal are accessible:
process()
-> item as T
name <- item.name()
Because T must be an Animal, calling name() is safe.
COMPILE-TIME SAFETY
Using an incompatible type is a compile error:
Handler of String // ERROR: String is not an Animal
Only Animal subtypes are accepted.
FUNCTION CONSTRAINTS
Generic functions also support constraints:
describe() of type T constrain by Animal -> item as T <- rtn as String: item.name()
See Q194 for generic classes. See Q58 for generic functions. See Q103 for abstract classes. See Q257 for constrained types (constrain as vs constrain by).
Example
defines module qa.generics.constraints defines class Animal as abstract animalName as String? default private Animal() as pure Animal() as pure -> n as String animalName :=? String(n) name() as pure <- rtn as String: String(animalName) override operator ? as pure <- rtn as Boolean: animalName? Dog is Animal Dog() as pure -> n as String super(n) Dog() as pure super("Rex") bark() as pure <- rtn as String: "Woof!" Cat is Animal Cat() as pure -> n as String super(n) Cat() as pure super("Whiskers") purr() as pure <- rtn as String: "Purr..." defines function // Generic function constrained to Animal subtypes describeAnimal() of type T constrain by Animal as open -> item as T <- rtn as String: item.name() defines program GenericConstraintsDemo() stdout <- Stdout() // === CONSTRAINED GENERIC FUNCTION === dog <- Dog() dogDesc <- describeAnimal(dog) stdout.println(`Dog: ${dogDesc}`) theCat <- Cat() catDesc <- describeAnimal(theCat) stdout.println(`Cat: ${catDesc}`) // The following would NOT compile: // describeAnimal("hello") // String is not an Animal stdout.println("Type constraints ensure only Animal subtypes are accepted")
Common mistakes
E50060 — The method 'describe()' does not exist on Dog. Use the generic function 'describeAnimal()' which works on any Animal subtype. See ek9 -h E50060 for details.
Incorrect:
dogDesc <- dog.describe()
Correct:
dogDesc <- describeAnimal(dog)
Other ways to ask this
- How do I restrict what types can be used with a generic in EK9?
- What is 'constrain by' in EK9 generics?
- How do I add type bounds to generics in EK9?
Coming from another language?
Java: <T extends Animal> for upper bounds. C#: where T : Animal. Rust: <T: Animal> trait bounds. Go: type T Animal constraint (Go 1.18+). Kotlin: <T : Animal> bounds. EK9: 'of type T constrain by Animal' reads as natural English, same syntax for functions and classes.
Keywords: parameterized, extends, type-parameter, subtype, constrain, restrict, type, safety, generic, bound, constraint, constant