How do I use Function and Routine for transformations?

← Generics · Ref: Q710

Function and Routine are the two-type-parameter transformation types. They take 't' of type T and return 'r' of type R.

FUNCTION IS PURE

Function of (T, R) takes parameter 't' and returns 'r'. Being pure, the transformation is deterministic:

  mapper <- () is Function of (String, Integer) as pure function
    r: length t

Always produces the same output for the same input.

ROUTINE IS IMPURE

Routine of (T, R) has the same signature but is NOT pure:

  logMapper <- () is Routine of (String, Integer) as function
    stdout <- Stdout()
    stdout.println(`Transforming: ${t}`)
    r: length t

Routine allows side effects during transformation.

PARAMETER USAGE

Accept Function for guaranteed pure transforms:

  transform() as pure
    -> item as String, mapper as Function of (String, Integer)
    <- result as Integer: mapper(item)

See Q54 for pure vs impure concepts. See Q89 for stream pipeline operations. See Q649 for generic function implementation.

Example

defines module qa.genericsdeep.functionroutine

  defines function

    <?-
      Helper that applies a pure Function transformation.
    -?>
    applyTransform() as pure
      ->
        item as String
        mapper as Function of (String, Integer)
      <- result as Integer: mapper(item)

    <?-
      Helper that applies an impure Routine transformation.
    -?>
    applyRoutine()
      ->
        item as String
        mapper as Routine of (String, Integer)
      <- result as Integer: mapper(item)

  defines program

    FunctionRoutineDemo()
      stdout <- Stdout()

      //Function: pure, deterministic transform
      //Body uses 't' for input, 'r' for return — from Function's signature
      lenMapper <- () is Function of (String, Integer) as pure function
        r: length t

      stdout.println(`Length of 'Hello': ${applyTransform("Hello", lenMapper)}`)

      //Another Function: Integer to String
      strMapper <- () is Function of (Integer, String) as pure function
        r: $t

      stdout.println(`42 as string: ${strMapper(42)}`)

      //Routine: impure, can log during transformation
      logMapper <- () is Routine of (String, Integer) as function
        stdout <- Stdout()
        stdout.println(`Transforming: ${t}`)
        r: length t

      result <- applyRoutine("World", logMapper)
      stdout.println(`Routine result: ${result}`)

Common mistakes

E05150 — Function is pure — dynamic functions extending Function must use 'as pure function', not just 'as function'. The compiler requires the purity of the implementation to match its pure super type. See ek9 -h E05150 for details.

Incorrect:

() is Function of (Integer, String) as function

Correct:

() is Function of (Integer, String) as pure function
Other ways to ask this
  • What is the difference between Function and Routine in EK9?
  • How do I create a Function of (String, Integer) for mapping?
  • When should I use Routine instead of Function?

Coming from another language?

Java: java.util.function.Function<T,R> — no purity distinction, can have side effects. Kotlin: (T) -> R — no pure/impure variants. Rust: Fn(T) -> R (pure) vs FnMut(T) -> R (stateful). Go: func(T) R — no purity enforcement. C#: Func<T, TResult> — no purity concept. EK9: Function (pure, deterministic transform) vs Routine (impure, can access state) — compile-time enforced.

Keywords: function, pure, function-type, map, side-effect, routine, mapping, transform, generic, impure, two-parameter