How do I define generics with multiple type parameters in EK9?

← Generics · Ref: Q196

EK9 supports multiple type parameters using parentheses: 'of type (A, B)'. This enables types like Pair, Either, and mapper functions with distinct input/output types.

MULTIPLE TYPE PARAMETERS

Use parentheses for multiple parameters (a generic type needs both a default and an inferred-type constructor, E06040):

  defines class
    Pair of type (A, B)
      first as A?
      second as B?
      Pair() as pure
        first :=? A()
        second :=? B()
      Pair() as pure
        ->
          a as A
          b as B
        first :=? a
        second :=? b
      default operator ?

INSTANTIATION

Provide both types explicitly with empty parentheses (this creates an unset Pair):

  pair <- Pair() of (String, Integer)

Or give the values and let EK9 infer the types:

  pair <- Pair("hello", 42)

Giving values AND an 'of' clause together is error E06220: choose either empty parentheses with a type definition, or values with inferred types.

GENERIC FUNCTIONS WITH MULTIPLE PARAMETERS

Functions can also use multiple type parameters:

  defines function
    mapper() of type (S, T) as abstract
      -> source as S
      <- target as T?

BUILT-IN EXAMPLES

Dict, Result, and DictEntry all use multiple type parameters:

  counts <- Dict() of (String, Integer)
  outcome <- Result() of (String, Integer)

See Q194 for single-parameter generics. See Q195 for constraints. See Q46 for Dict.

See Q654 for parameter count validation. See Q655 for method overloading with generics.

Example

#!ek9
defines module qa.generics.multitype

  defines class

    Pair of type (A, B)
      first as A?
      second as B?

      Pair() as pure
        first :=? A()
        second :=? B()

      Pair() as pure
        ->
          a as A
          b as B
        first :=? a
        second :=? b

      getFirst()
        <- rtn as A: A()
        rtn :=? A(first)

      getSecond()
        <- rtn as B: B()
        rtn :=? B(second)

      default operator ?

  defines program

    MultiParamGenericDemo()
      stdout <- Stdout()

      // === INSTANTIATION WITH INFERENCE ===

      pair <- Pair("hello", 42)
      stdout.println(`Pair is set: ${pair?}`)

      // === ACCESSING VALUES ===

      first <- pair.getFirst()
      if first?
        stdout.println(`First: ${first}`)

      second <- pair.getSecond()
      if second?
        stdout.println(`Second: ${second}`)

      // === DIFFERENT TYPE COMBINATIONS ===

      boolFloat <- Pair(true, 3.14)
      stdout.println(`Bool-Float pair set: ${boolFloat?}`)

Common mistakes

E06220 — A constructor call that supplies values cannot also carry an of type clause: choose either empty parentheses with a type definition, Pair() of (String, Integer), or values with inferred types, Pair("hello", 42). See ek9 -h E06220 for details.

Incorrect:

pair <- Pair("hello") of String

Correct:

pair <- Pair("hello", 42)

E06030 — A generic class constructor parameter count must match the number of type parameters. Pair of type (A, B) needs a constructor accepting both A and B. See ek9 -h E06030 for details.

Incorrect:

a as A

Correct:

a as A
          b as B
Other ways to ask this
  • Can EK9 generics have more than one type parameter?
  • How do I create a Pair type with two generic parameters?
  • What is the syntax for multi-parameter generics in EK9?

Coming from another language?

Java: class Pair<A, B> with angle brackets. C++: template<typename A, typename B>. Rust: struct Pair<A, B>. Go: type Pair[A, B any]. Kotlin: class Pair<A, B>. EK9: 'Pair of type (A, B)' with parentheses for multiple parameters, reads as natural English.

Keywords: define, generic, multi, multiple, pair, parameter, parameterized, parentheses, tuple, type, type-parameter