Can dynamic functions extend abstract functions?

← Function Extension · Ref: Q590

Yes, dynamic functions can extend abstract functions. A dynamic function is created inline and implements the abstract function's contract, typically capturing values from the surrounding scope.

DYNAMIC FUNCTION SYNTAX

Create a dynamic function that extends an abstract:

  Formatter as pure abstract
    -> text as String
    <- rtn as String?
  upper <- () is Formatter as pure (rtn:=? text.upperCase())

CLOSURE CAPTURE

Dynamic functions capture values from their enclosing scope. The captured values become part of the function instance:

  tag <- "LOG"
  logger <- (tag) is Formatter as pure function
    rtn:=? tag + ": " + text

INLINE VS MULTI-LINE

Inline form: () is Type as pure (body). Multi-line form: () is Type as pure function followed by indented body.

WHEN TO USE DYNAMIC FUNCTIONS

One-off implementations that do not warrant a named function. Functions that need access to local variables (closures). Building function instances with different captured state.

See Q52 for dynamic function basics. See Q53 for closure capture. See Q588 for function extension basics. See Q591 for comparing function and class hierarchies.

Example

defines module qa.function.dynamicextends

  defines function

    //Abstract function contract
    Formatter as pure abstract
      -> text as String
      <- rtn as String?

    //Named implementation for comparison
    PlainFormatter is Formatter as pure
      -> text as String
      <- rtn as String: text

  defines program

    DynamicFunctionExtendsDemo()
      stdout <- Stdout()

      //Named implementation
      stdout.println(PlainFormatter("hello"))

      //Inline dynamic function (single-line body)
      upper <- () is Formatter as pure (rtn:=? text.upperCase())
      stdout.println(upper("hello"))

      //Dynamic function with captured variable (multi-line body)
      tag <- "LOG"
      logger <- (tag) is Formatter as pure function
        rtn:=? `${tag}: ${text}`

      stdout.println(logger("system started"))

      //Another capture with different value
      errorTag <- "ERROR"
      errorLogger <- (errorTag) is Formatter as pure function
        rtn:=? `${errorTag}: ${text}`

      stdout.println(errorLogger("disk full"))

      //All are Formatter type: polymorphic usage
      formatters <- List() of Formatter
      formatters += PlainFormatter
      formatters += upper
      formatters += logger
      formatters += errorLogger

      for item in formatters
        stdout.println(item("test message"))

Common mistakes

E50001 — A pure function cannot call non-pure methods. Pure functions must not produce side effects. See ek9 -h E50001 for details.

Incorrect:

PlainFormatter is Formatter as pure
      -> text as String
      <- rtn as String: text
      stdout.println(text)

Correct:

PlainFormatter is Formatter as pure
      -> text as String
      <- rtn as String: text
Other ways to ask this
  • How do I create inline function implementations?
  • Can I use dynamic functions with abstract function types?
  • How do I extend an abstract function inline?

Coming from another language?

Java: lambdas implement functional interfaces. Python: lambdas are limited to expressions. Rust: closures implement Fn traits. Kotlin: lambdas implement functional types. EK9: dynamic functions extend abstract functions with closure capture.

Keywords: anonymous, implement, extend, extends, closure, dynamic, inline, function, abstract, capture