Why must generic functions provide an implementation?

← Generics · Ref: Q649

EK9 requires generic functions to provide an implementation. Unlike abstract methods in classes, generic functions are templates that must contain code to execute when instantiated.

IMPLEMENTATION REQUIRED

A generic function must have a body:

  transform() of type T as open
    -> item as T
    <- rtn as T: T(item)

The body provides the default behavior.

AS OPEN FOR EXTENSION

Mark a generic function 'as open' to allow specialised implementations:

  compare() of type T as open
    -> a as T, b as T
    <- rtn as Integer: a <=> b

Concreted implementations can override this default.

DYNAMIC INSTANTIATION

Generic functions create concrete instances:

  intCompare <- () is compare of Integer as function

This uses the template's default implementation.

CUSTOM IMPLEMENTATION

Dynamic functions can provide custom bodies:

  customCompare <- () is compare of Integer as function
    rtn: b <=> a  // reversed

See Q197 for extending generics. See Q647 for function constraint rules. See Q194 for generic class basics.

Example

defines module qa.genericsdeep.functionimplementation

  defines function

    <?-
      Correct: generic function with implementation body.
      'as open' allows extension when parameterised.
    -?>
    doubleValue() of type T as open
      -> item as T
      <- rtn as T: item + item

    <?-
      Correct: multi-param generic function with body.
    -?>
    selectFirst() of type (A, B) as open
      ->
        first as A
        second as B
      <- rtn as A: A(first)
      require second?

    <?-
      Correct: constrained generic function with implementation.
    -?>
    stringify() of type T as open
      -> item as T
      <- rtn as String: $item

  defines program

    GenericFunctionImplDemo()
      stdout <- Stdout()

      // === USING DEFAULT IMPLEMENTATIONS ===

      doubled <- doubleValue(21)
      stdout.println(`Doubled: ${doubled}`)

      strDoubled <- doubleValue("ab")
      stdout.println(`String doubled: ${strDoubled}`)

      // === MULTI-PARAM GENERIC FUNCTION ===

      selected <- selectFirst("chosen", 99)
      stdout.println(`Selected: ${selected}`)

      // === STRINGIFY GENERIC ===

      asStr <- stringify(3.14)
      stdout.println(`Stringified: ${asStr}`)

Common mistakes

E07110 — Generic functions must provide an implementation body. Unlike abstract methods in classes, generic functions are templates that generate concrete code when instantiated with specific types. A generic function without a body has nothing to instantiate. See ek9 -h E07110 for details.

Incorrect:

doubleValue() of type T as open
      -> item as T
      <- rtn as T?

Correct:

doubleValue() of type T as open
      -> item as T
      <- rtn as T: item + item
Other ways to ask this
  • What is E06100 generic function implementation required?
  • Can I declare a generic function without a body?
  • How do generic function templates work in EK9?

Coming from another language?

Java: abstract methods exist but generic methods always have bodies. C++: templates always have bodies (no separate declaration for function templates). Rust: generic functions always have bodies. Go: generic functions always have bodies. Kotlin: generic functions always have bodies. EK9: same requirement, enforced with E06100 if body missing.

Keywords: function, open, implementation, type-parameter, required, E06100, generic, template, body