How must constrained type constructors match the parameterising type?
← Generics · Ref: Q651
When a generic type is constrained with 'constrain by', the constraining type's constructors must be present on any type used to parameterise the generic. If the constraining type has a String constructor, any actual type argument must also have a String constructor.
CONSTRUCTOR MATCHING
If Shape has 'Shape(name as String)', then any T used in 'of type T constrain by Shape' must also have a String constructor.
WHY REQUIRED
Generic code may create instances of T using constructors from the constraining type. If the constructor does not exist, the code would fail at instantiation.
CORRECT PATTERN
Ensure subtypes mirror the constructor signatures:
defines class Shape as abstract shapeName as String: String() Shape() -> n as String shapeName: n
default operator ? Circle extends Shape Circle() // matches Shape's constructor -> n as String super(n)
See Q195 for constrain by basics. See Q642 for constructor inference. See Q647 for function constraint limits.
Example
defines module qa.genericsdeep.constraintconstructormatch defines class <?- Abstract base class used as constraint. Has both default and String constructors. -?> Vehicle as abstract vehicleName as String: String() default Vehicle() as pure Vehicle() as pure -> n as String vehicleName :=? String(n) name() as pure <- rtn as String: String(vehicleName) default operator ? <?- Concrete subtype with matching constructors. Both default and String constructors present (matching Vehicle). -?> Truck is Vehicle payload <- Integer() default Truck() as pure Truck() as pure -> n as String super(n) Truck() as pure -> n as String cap as Integer super(n) payload :=? cap override operator ? as pure <- rtn as Boolean: name()? <?- Subtype missing String constructor (for E06130 mutation testing). -?> Bicycle is Vehicle default Bicycle() as pure override operator ? as pure <- rtn as Boolean: name()? <?- Another concrete subtype with matching constructors. -?> Sedan is Vehicle doors as Integer: 4 default Sedan() as pure Sedan() as pure -> n as String super(n) override operator ? as pure <- rtn as Boolean: name()? defines function <?- Constrained generic function. T must be a Vehicle, so T has name() method available. -?> describeVehicle() of type T constrain by Vehicle as open -> item as T <- rtn as String: item.name() defines program ConstraintConstructorDemo() stdout <- Stdout() truck <- Truck("Big Rig") truckDesc <- describeVehicle(truck) stdout.println(`Truck: ${truckDesc}`) sedan <- Sedan("Family Car") sedanDesc <- describeVehicle(sedan) stdout.println(`Sedan: ${sedanDesc}`)
Common mistakes
E06130 — Bicycle extends Vehicle but is missing the String constructor that Vehicle defines. When describeVehicle (constrained by Vehicle) is parameterised with Bicycle, the compiler detects the missing constructor match and triggers E06130. See ek9 -h E06130 for details.
Incorrect:
bicycle <- Bicycle() bicycleDesc <- describeVehicle(bicycle) stdout.println(`Bicycle: ${bicycleDesc}`)
Correct:
sedan <- Sedan("Family Car") sedanDesc <- describeVehicle(sedan) stdout.println(`Sedan: ${sedanDesc}`)
Other ways to ask this
- What is E06130 constrained type constructor missing?
- Why must constrained generic subtypes have matching constructors?
- How do constructor requirements flow from constraints to subtypes?
Coming from another language?
Java: no constructor inheritance or matching requirement (erasure). C++: concepts can require constructibility. Rust: trait bounds define method requirements, not constructors. Go: no constructor concept. Kotlin: no constructor matching for generics. EK9: constraining type constructors must exist on parameterising type.
Keywords: E06130, constructor, parameter, match, subtype, generic, type-parameter, missing, constant, constraint