What are the rules for when a function can or cannot be extended?

← Function Extension · Ref: Q594

Only abstract functions can be extended. Concrete functions (those with an implementation body) are closed and cannot serve as a base for other functions.

CAN EXTEND: ABSTRACT FUNCTIONS

Functions declared with 'as abstract' can be extended:

  Predicate as pure abstract
    -> item as String
    <- result as Boolean?

CANNOT EXTEND: CONCRETE FUNCTIONS

Functions with a body cannot be extended:

  addOne() as pure
    -> x as Integer
    <- rtn as Integer: x + 1
  //Cannot write: DoubleAddOne is addOne

SIGNATURE MUST MATCH (E05140)

The extending function must match the abstract function's parameter types, count, and return type. Mismatches produce E05140.

PURITY MUST BE COMPATIBLE

If the abstract function is pure, implementations must be pure (same E05150 rule as classes). If the abstract function is not pure, implementations can be either.

See Q588 for function extension basics. See Q592 for signature mismatch. See Q593 for multi-level chains. See Q595 for pure abstract function constraints.

Example

defines module qa.function.extensionrules

  defines function

    //Abstract: CAN be extended
    Converter as pure abstract
      -> input as Integer
      <- output as String?

    //Implementation 1
    DecimalConverter is Converter as pure
      -> input as Integer
      <- output as String: $input

    //Implementation 2
    HexConverter is Converter as pure
      -> input as Integer
      <- output as String: "0x" + $input

    //Concrete function: CANNOT be extended
    doubleIt() as pure
      -> number as Integer
      <- result as Integer: number + number

  defines program

    FunctionExtensionRulesDemo()
      stdout <- Stdout()

      //Abstract function implementations
      converters <- List() of Converter
      converters += DecimalConverter
      converters += HexConverter

      for converter in converters
        stdout.println(converter(255))

      //Concrete function: used directly
      stdout.println(`Double 5: ${doubleIt(5)}`)

Common mistakes

E50001 — A pure function cannot call non-pure methods. The 'as pure' contract forbids all side effects. See ek9 -h E50001 for details.

Incorrect:

doubleIt() as pure
      -> number as Integer
      <- result as Integer: number + number
      stdout.println(result)

Correct:

doubleIt() as pure
      -> number as Integer
      <- result as Integer: number + number
Other ways to ask this
  • When can I extend a function in EK9?
  • What functions can be used as a base type?
  • Why can't I extend a concrete function?

Coming from another language?

Java: functional interfaces can only have one abstract method. Kotlin: function types are structural, no extension. Rust: no function type inheritance. EK9: only abstract functions can be extended, concrete functions are closed, E05140 for signature mismatch.

Keywords: immutable, purity, E05150, extend, function, extension, E05140, abstract, side-effect, rules, concrete, closed