What is the correct syntax for generic type parameterization and instantiation?
← Generics · Ref: Q681
EK9 uses 'of type T' syntax for declaring generic type parameters and 'of TypeName' for instantiating them. Correct usage requires matching the number of type parameters.
DECLARATION SYNTAX
Generic type parameters use 'of type'. 'Container of type T' declares one parameter and 'Mapping of type (K, V)' declares two. The parameter names become placeholders for concrete types.
INSTANTIATION SYNTAX
Concrete types replace parameters with 'of'. 'Container of String' supplies one and 'Mapping of (String, Integer)' supplies two. The number of concrete types must match the declared parameters.
MISSING PARAMETERS (E06010)
Omitting required type parameters triggers E06010:
defines class Container of type T item as T: T() default operator ? defines program Demo() container <- Container() // ERROR E06010: needs a type unless inferred from args
The correct form gives the explicit type:
container <- Container() of String
VALUES PLUS 'of' (E06220)
Supplying constructor values AND an explicit 'of' clause together is E06220:
defines class Container of type T item as T: T() Container() -> value as T this.item: value default operator ? defines program Demo() container <- Container(42) of Integer // ERROR E06220: infer the type from the value, or use Container() of Integer
The correct form lets T be inferred as Integer:
container <- Container(42)
INFERRENCE FROM ARGUMENTS
When constructor arguments match type parameters, inference works:
container <- Container("hello") // T inferred as String
PARENTHESES FIRST (E06210)
'Container of String()' puts the '()' after the type arguments; write 'Container() of String' - the compiler names the fix and the editing tools rewrite it.
TARGET TYPING
'x <- Container() of String' is the form to write; where the target's type is already declared, a bare construction is completed from it - 'container as Container of String: Container()', and the same for ':=', ':=?', a returning variable, a property or a field reached through an object ('this.box := Container()') - but never with '<-', which declares no type, and not in a ternary arm or an argument.
See Q642 for constructor inference. See Q654 for parameter count validation. See Q656 for argument count validation.
Example
defines module qa.genericsdeep.parameterizationsyntax defines class <?- Single type parameter generic. Demonstrates correct 'of type T' declaration syntax. -?> Holder of type T content as T? Holder() as pure content :=? T() Holder() as pure -> initialContent as T content :=? initialContent getContent() <- rtn as T: T() rtn :=? T(content) hasContent() as pure <- rtn as Boolean: content? default operator ? <?- Multi-parameter generic. Both parameters must be specified at instantiation. -?> Pair of type (A, B) first as A? second as B? Pair() as pure first :=? A() second :=? B() Pair() as pure -> first as A second as B this.first :=? first this.second :=? second getFirst() <- rtn as A: A() rtn :=? A(first) getSecond() <- rtn as B: B() rtn :=? B(second) default operator ? defines program GenericParameterizationDemo() stdout <- Stdout() //Inference from single argument intHolder <- Holder(42) stdout.println(`Integer holder set: ${intHolder?}`) //Type declaration (no parentheses on lhs) typedHolder as Holder of String: Holder("typed") stdout.println(`Typed holder: ${typedHolder?}`) //Explicit type parameter (parentheses required for constructor) emptyHolder <- Holder() of String stdout.println(`Empty holder set: ${emptyHolder?}`) //A declared type completes a bare construction targetHolder as Holder of Integer: Holder() stdout.println(`Target-typed holder set: ${targetHolder?}`) //Inference from multiple arguments entry <- Pair("key", 100) stdout.println(`Pair set: ${entry?}`) //Explicit multi-parameter emptyPair <- Pair() of (Float, Boolean) stdout.println(`Empty pair set: ${emptyPair?}`)
Common mistakes
E50060 — Holder has no toString() method. Use the $ prefix operator or ? for boolean check. See ek9 -h E50060 for details.
Incorrect:
stdout.println(intHolder.toString())
Correct:
stdout.println(`Integer holder set: ${intHolder?}`)
E06010 — Generic type instantiation must follow the correct syntax. Using incorrect call syntax when creating a parameterised type triggers E06010. See ek9 -h E06010 for details.
Incorrect:
intHolder <- Holder()(42)
Correct:
intHolder <- Holder(42)
E06010 — When no constructor arguments allow type inference, the type parameter must be explicitly specified. Omitting it triggers E06010 because the compiler cannot determine T. See ek9 -h E06010 for details.
Incorrect:
emptyHolder <- Holder()
Correct:
emptyHolder <- Holder() of String
E06200 — Parentheses are not allowed in type declarations on the left-hand side. Use 'Holder of String' not 'Holder() of String' for type references. See ek9 -h E06200 for details.
Incorrect:
typedHolder as Holder() of String: Holder("typed")
Correct:
typedHolder as Holder of String: Holder("typed")
E06210 — The parentheses belong to the generic's name, before the type arguments: 'Holder of String()' reads as a call of the type 'Holder of String'. Write 'Holder() of String'. See ek9 -h E06210 for details.
Incorrect:
emptyHolder <- Holder of String()
Correct:
emptyHolder <- Holder() of String
E06210 — Parentheses are required when constructing a generic type. Use 'Holder() of String' not 'Holder of String' for constructors. See ek9 -h E06210 for details.
Incorrect:
emptyHolder <- Holder of String
Correct:
emptyHolder <- Holder() of String
Other ways to ask this
- What is E06200 generic type reference error?
- What is E06210 generic type instantiation error?
- What is E06010 missing generic type parameter?
- How do I correctly reference and create generic types?
Coming from another language?
Java: angle brackets List<String>. Rust: turbofish Vec::<String>. Go: brackets List[String]. Python: hints List[str]. EK9: 'of' syntax - List of String, Dict of (String, Integer).
Keywords: of, syntax, parameterization, E06200, E06220, type, generic, instantiation, type-parameter, E06210