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