How do I declare a variable in EK9?

← Getting Started · Ref: Q22

EK9 provides several ways to declare variables and uses modifiers to control visibility, purity, and extensibility.

VARIABLE DECLARATIONS

1. Type inference with <- (idiomatic)

  name <- "Steve"           Inferred as String
  count <- 42                Inferred as Integer

2. Explicit type

  name as String: "Steve"

Use when declaring base types for polymorphism.

3. Reassignment operators

  name <- "Steve"       Declaration (new variable)
  name := "John"        Reassignment (existing variable)
  name :=? "Default"    Guarded: only if currently unset

4. Class/record fields

  greeting as String: "Hello"    Private by default

5. Parameters and returns (explicit types required)

  formatName() as pure
    -> first as String
    <- result as String: first

6. Named parameters

  result <- formatName(first: "Steve", last: "Limb")

If ONE is named, ALL must be named, in declared order. Essential for dynamic functions:

  multiplier <- (factor: count) is abstractOp as pure function
    result :=? operand * factor

MODIFIERS

  public/protected/private   Access (default: package-private)
  pure         No side effects, enforced on overrides
  abstract     Must be overridden
  override     Required when replacing parent method
  open         Allows extension (closed by default)
  default      Auto-generates operators
  dispatcher   Multi-method dispatch by argument type
  sanitized    Input sanitization at call site

Naming: generic names ('value', 'data', 'item') rejected (E11031).

See Q49 for modifiers on functions. See Q93 for classes. See Q97 for records. See Q215 for sanitized. See Q28 for multiple variables. See Q127 for type inference. See Q290 for banned names. See Q292 for naming conventions.

Example

defines module qa.variables.declarations

  defines function

    formatName() as pure
      ->
        first as String
        last as String
      <- result as String: `${first} ${last}`

    abstractOp() as pure abstract
      -> operand as Integer
      <- result as Integer?

  defines class

    Greeter
      greeting as String: "Hello"

      Greeter()
        -> g as String
        greeting :=: g

      greet()
        -> name as String
        <- message as String: `${greeting}, ${name}`

      default operator ?

  defines program
    Declarations()
      stdout <- Stdout()

      // Type inference - idiomatic for local variables
      name <- "Steve"
      count <- 42

      // Explicit type - useful when declaring a base type
      label as String: "Count"

      // Reassignment
      name := "John"

      // Using the explicitly typed variable
      stdout.println(`${label}: ${count}`)

      // Named parameters at call site
      full <- formatName(first: name, last: "Limb")
      stdout.println(full)

      // Dynamic function with named captures
      multiplier <- (factor: count) is abstractOp as pure function
        result :=? operand * factor

      stdout.println($multiplier(3))

      greeter <- Greeter("Welcome")
      stdout.println(greeter.greet(name))

Common mistakes

E08180 — Class fields must be initialised inline with a value. An uninitialised field triggers E08180. Use a colon followed by a literal or constructor call. See ek9 -h E08180 for details.

Incorrect:

greeting as String

Correct:

greeting as String: "Hello"

E07520 — The ? operator is inherited from the base type and must use 'default' to auto-generate or 'override' for a custom implementation. Declaring bare 'operator ?' triggers E07520 because operator semantics require a Boolean return. See ek9 -h E07520 for details.

Incorrect:

operator ?

Correct:

default operator ?

E50001 — A typo in the function name 'formatNam' means the compiler cannot find any identifier with that name in scope. EK9 requires exact spelling for all identifiers. See ek9 -h E50001 for details.

Incorrect:

full <- formatNam(first: name, last: "Limb")

Correct:

full <- formatName(first: name, last: "Limb")
Other ways to ask this
  • What are the different ways to declare variables in EK9?
  • How does type inference work for local variables?
  • What modifiers does EK9 support?
  • What is the difference between <- and := and :=? operators?

Coming from another language?

Java: Type name = value with all classes open by default, Python: name = value with duck typing, Rust: let name: Type = value with immutability by default, Go: var name Type = value or name := value. EK9: <- for declaration with inference, explicit Type for API boundaries, := for reassignment, :=? for guarded assignment. Modifiers: Java has public/private/protected/abstract/final/static, EK9 replaces final with closed-by-default and has no static. EK9 adds pure, dispatcher, sanitized, and open.

Keywords: abstract, immutable, intro, sanitized, dispatcher, migrate, pure, guard, named, private, protected, default, variable, beginner, first, start, isset, parameter, open, declare, modifier, assignment, side-effect, return, safe, override, virtual, declaration, inference, null-safe, public