How do return values work in EK9 functions?

← Syntax and Structure Rules · Ref: Q917

EK9 uses ONE <- return declaration per function. There is NO return keyword. You declare a named return variable, give it a default value, then update it on different code paths.

BASIC PATTERN

  classify() as pure
    -> score as Integer
    <- label as String: "average"
    if score >= 90
      label: "excellent"
    else if score < 40
      label: "poor"

The variable 'label' is declared with <- and default "average". Different code paths update it with the : assignment operator. The compiler ensures all paths initialise the variable.

RULES

1. ONE <- per function (not multiple returns)
2. The return variable MUST have a default value or be initialised on ALL paths
3. Use : to update the return variable on conditional paths
4. There is NO return keyword — the named variable is automatically returned

NO EARLY RETURN

Instead of returning early, structure your logic so the return variable gets the right value:

  process() as pure
    -> text as String
    <- rtn as String: "empty"
    if text?
      rtn: text.upperCase()

If text is unset, rtn stays "empty". If set, rtn gets the uppercase version.

MULTIPLE RETURN VALUES

If you need multiple return values, return a record or class:

  splitName() as pure
    -> fullName as String
    <- parts as NameParts: NameParts()

WRONG PATTERNS

  <- a as String: "x"
  <- b as String: "y"       WRONG: only one <- allowed
  return "hello"             WRONG: no return keyword

See Q274 for why return was removed. See Q916 for parameter syntax. See Q50 for declared return variables.

Example

defines module qa.syntaxrules.onereturn

  defines function

    // Simple return with default
    greet() as pure
      -> name as String
      <- message as String: "Hello, " + name

    // Conditional paths update the return variable
    classify() as pure
      -> score as Integer
      <- label as String: "average"

      excellentThreshold <- 90
      poorThreshold <- 40

      if score >= excellentThreshold
        label: "excellent"
      else if score < poorThreshold
        label: "poor"

    // Guard pattern instead of early return
    safeUpperCase() as pure
      -> text as String
      <- rtn as String: "empty"

      if text?
        rtn: text.upperCase()

  defines program

    OneReturnDemo()
      stdout <- Stdout()

      // === Simple return ===

      stdout.println(greet("Steve"))

      // === Conditional return ===

      stdout.println(`Score 95: ${classify(95)}`)
      stdout.println(`Score 30: ${classify(30)}`)
      stdout.println(`Score 60: ${classify(60)}`)

      // === Guard pattern ===

      stdout.println(`Upper: ${safeUpperCase("hello")}`)
      stdout.println(`Empty: ${safeUpperCase(String())}`)

Common mistakes

E01083 — EK9 allows only ONE <- return declaration per function. Multiple <- arrows trigger E01083. Declare a single named return variable and update it on different code paths. See ek9 -h E01083 for details.

Incorrect:

<- label as String: "average"
      <- extra as Integer: 0

Correct:

<- label as String: "average"
Other ways to ask this
  • Can I have multiple return statements in EK9?
  • How do I declare a return value in EK9?
  • What does <- mean for returns in EK9?

Coming from another language?

Java: return statement, multiple returns common. Python: return statement, multiple returns common. Rust: implicit last expression or return. Go: named returns exist but return still needed. Kotlin: return statement. EK9: ONE named return variable with <-, NO return keyword, compiler verifies all paths.

Keywords: value, return, declaration, variable, function, arrow, named, single