When do I use <- and when do I use :=? to set a variable safely?

← Control Flow · Ref: Q1039

They solve different problems:

<- CREATES a new variable. Use it when the variable does not exist yet.

  name <- "Alice"           creates name, assigns "Alice"
  if greeting <- getGreeting()  creates greeting in this scope, only enters if SET

:=? REASSIGNS an existing variable, but only if it is currently UNSET. Use it when the variable already exists and you want to fill in a missing value without overwriting an existing one.

  name <- String()           creates name (unset)
  name :=? loadFromConfig()  assigns only if name is still unset
  name :=? "default"         assigns only if still unset after config

Key distinction:

  <- is about CREATION. The variable must not exist yet.
  :=? is about SAFE UPDATE. The variable must already exist. It protects existing values.

Common mistake: using <- when you mean :=?

  name <- loadFromConfig()      WRONG if name already exists in scope
  name :=? loadFromConfig()     RIGHT — safely fills in if unset

Common mistake: using :=? when you mean <-

  name :=? "Alice"              WRONG if name does not exist yet
  name <- "Alice"               RIGHT — creates the variable

In guards (if, switch, while):

  if name <- getGreeting()      declaration guard — creates name, enters if SET
  if name :=? getGreeting()     guarded assign — fills name if unset, enters if SET

Example

defines module qa.controlflow.declare.vs.guardedassign

  defines function

    loadFromConfig()
      <- rtn as String: String()
      //Simulate: config returns unset

    loadFromEnvironment()
      <- rtn as String: "env-value"

  defines program

    DeclareVsGuardedDemo()
      stdout <- Stdout()

      // <- creates a new variable
      setting <- String()

      // :=? fills in if unset (config returns unset, so still unset)
      setting :=? loadFromConfig()

      // :=? fills in from next source (environment returns "env-value")
      setting :=? loadFromEnvironment()

      // :=? preserves existing value (already set, so "fallback" is ignored)
      setting :=? "fallback"

      stdout.println(`Setting: ${setting}`)

      // Contrast: <- in a guard creates a NEW variable
      if greeting <- loadFromEnvironment()
        stdout.println(`Greeting: ${greeting}`)

Common mistakes

E50001 — Use <- to create a new variable; :=? only reassigns a variable that already exists, so using it first leaves the name unresolved. See ek9 -h E50001 for details.

Incorrect:

      setting :=? String()

Correct:

      setting <- String()
Other ways to ask this
  • What is the difference between <- and :=? in EK9?
  • Should I use declaration or guarded assignment here?
  • How do I choose between <- for new variables and :=? for safe reassignment?

Coming from another language?

No other language has this distinction. Python's walrus := creates AND assigns. EK9 separates creation (<-) from safe update (:=?).

Keywords: declare, safe, assign, guarded, unset, create, difference