How does constructor-based type inference work for generic classes?
← Generics · Ref: Q642
EK9 infers generic type parameters from constructor arguments. For inference to work, the number of constructor parameters must match the number of type parameters, and each constructor parameter type must correspond to the type parameter in the same position.
INFERRENCE RULE
A generic class 'Box of type T' with constructor 'Box(val as T)' allows inference:
box <- Box(42)
The compiler sees Integer argument, infers T = Integer.
MATCHING REQUIREMENT
For multi-parameter generics, parameter count must match:
Pair of type (A, B) Pair(a as A, b as B)
Now 'Pair("hello", 42)' infers A=String, B=Integer.
EXPLICIT ALTERNATIVE
When inference cannot work, specify types explicitly:
container <- Container() of String
See Q194 for generic class basics. See Q196 for multi-parameter generics. See Q643 for two-constructor requirement. See Q644 for constructor parameter type matching.
See Q681 for generic parameterization syntax. See Q682 for generic encapsulation. See Q683 for generic constructor rules.
Example
defines module qa.genericsdeep.constructorinference defines class <?- Single type parameter with matching constructor. Enables type inference from constructor argument. -?> Box of type T content as T? Box() as pure content :=? T() Box() as pure -> initialValue as T content :=? initialValue getContent() <- rtn as T: T() rtn :=? T(content) default operator ? <?- Multi-parameter generic with matching constructor. Two type params, two constructor params, in order. -?> Mapping of type (K, V) theKey as K? theVal as V? Mapping() as pure theKey :=? K() theVal :=? V() Mapping() as pure -> k as K v as V theKey :=? k theVal :=? v key() <- rtn as K: K() rtn :=? K(theKey) val() <- rtn as V: V() rtn :=? V(theVal) default operator ? defines program GenericConstructorInferenceDemo() stdout <- Stdout() // === INFERENCE FROM SINGLE ARGUMENT === intBox <- Box(42) stdout.println(`Integer box set: ${intBox?}`) strBox <- Box("hello") stdout.println(`String box set: ${strBox?}`) // === INFERENCE FROM MULTIPLE ARGUMENTS === entry <- Mapping("name", 42) stdout.println(`Mapping set: ${entry?}`) // === EXPLICIT WHEN NO ARGS === emptyBox <- Box() of String stdout.println(`Empty box set: ${emptyBox?}`)
Common mistakes
E06030 — Generic constructor type inference requires the number of constructor parameters to match the number of type parameters. Box has one type parameter T, so the inferred constructor must have exactly one parameter of type T. Adding extra parameters breaks inference. See ek9 -h E06030 for details.
Incorrect:
Box of type T content as T? default Box() as pure Box() as pure -> initialValue as T extra as Integer
Correct:
Box of type T content as T? Box() as pure content :=? T() Box() as pure
Other ways to ask this
- How does EK9 infer generic type parameters from constructor arguments?
- What is E06030 generic constructor inappropriate?
- Why must constructor parameter count match type parameter count for inference?
Coming from another language?
Java: type inference via diamond operator (<>) and constructor args. Rust: turbofish syntax (::<Type>) when inference fails. Go: type inference from function arguments (Go 1.18+). Kotlin: inferred from constructor args or explicit <Type>. EK9: inference from constructor args when param count matches type param count, explicit 'of Type' otherwise.
Keywords: match, inference, E06030, instantiation, type-parameter, generic, class, type, constructor, constant, parameter