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:
defines function describe() of type T constrain by Animal as open -> 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
#!ek9 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: bound, constant, constrain, constraint, extends, generic, parameterized, restrict, safety, subtype, type, type-parameter