Write a generic Pair class with two type parameters A and B that holds two values.

← Generics · Ref: Q1268

Generic classes with multiple type parameters use 'of type (A, B)' syntax. Key rules:

1. MUST have a no-arg constructor that gives each field an unset value: 'Pair() as pure' with 'first :=? A()' and 'second :=? B()'. A public 'default Pair() as pure' is rejected while 'first as A?' and 'second as B?' are uninitialised (E07175, plus E08060 for each field); 'default private Pair() as pure' is the alternative
2. MUST have a parameterised constructor with both type params
3. The parameterised constructor MUST be PUBLIC — a private one triggers E06060. A protected constructor is never allowed (E06060). Only the no-arg constructor may be private, either 'default private Pair() as pure' or a hand-written 'private Pair() as pure' with a body
4. If any constructor is pure, ALL must be pure (E05190)
5. Use 'operator $' (not 'override operator $') since there is no parent $ to override
6. Use 'default operator ?' for auto-generated isSet semantics

SYNTAX

  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
    operator $ as pure
      <- rtn as String: ...
    default operator ?

See Q194 for single-parameter generics. See Q1286 for why generic constructors must be public. See Q1243 for two-param Transformer example.

Example

defines module qa.genericsdeep.pairtwoparams

  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

      operator $ as pure
        <- rtn as String: `(${$first}, ${$second})`

      default operator ?

  defines program

    PairDemo()
      stdout <- Stdout()

      nameAge <- Pair("Alice", 30)
      stdout.println(`Pair: ${nameAge}`)

      coordPair <- Pair(3.14, 2.72)
      stdout.println(`Coordinates: ${coordPair}`)

      if nameAge?
        stdout.println("Pair is set")

Common mistakes

E06060 — A generic type's parameterised constructor must be public so the type can be instantiated (and inferred) with any valid type parameters; making it private triggers E06060, and a protected constructor is never allowed. Only the no-arg constructor may be private. See ek9 -h E06060 for details.

Incorrect:

      private Pair() as pure
        ->
          a as A

Correct:

      Pair() as pure
        ->
          a as A

E07110 — A non-abstract constructor must provide a body; removing the no-arg constructor's body leaves it with no implementation while it is not declared abstract. See ek9 -h E07110 for details.

Incorrect:

      Pair() as pure

Correct:

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

E05110 — Generic classes have no parent $ operator to override. Use 'operator $' without 'override'. See ek9 -h E05110 for details.

Incorrect:

      override operator $ as pure

Correct:

      operator $ as pure
Other ways to ask this
  • Define a generic Pair of type (A, B) with a constructor, operator ?, and operator $.
  • How do I create a generic class with two type parameters in EK9?
  • Show me a Pair class that holds first and second values of different types.
  • In Java I'd write Pair<A, B> — what is the EK9 equivalent?

Coming from another language?

Java: class Pair<A, B> { A first; B second; }. Kotlin: data class Pair<A, B>(val first: A, val second: B). Rust: struct Pair<A, B> { first: A, second: B }. Python: from typing import Generic, TypeVar. EK9: 'Pair of type (A, B)' with explicit constructors, 'default operator ?' and 'operator $'.

Keywords: A, B, of type, default operator, generic, constructor, pair, two type parameters