Which ways of combining the guard operators does the compiler reject, and what should I write instead?

← Control Flow · Ref: Q1982

Each pre-flow operator says one thing, and three combinations say something you did not mean. The compiler rejects them.

1. :=? ON A VARIABLE THAT IS ALWAYS SET (E08095)

  setting <- 7
  setting :=? fetchReading()     // can never assign

':=?' fills an UNSET variable. When the compiler can prove the variable is set, the ':=?' can never apply (and its right side is never even evaluated). Start from a value that may be unset ('setting <- fetchNothing()'), or assign with ':='.

2. A ':=' HEAD WITH NO CONDITION (E07340)

  if latest := fetchReading()

':=' in a head assigns and checks nothing, so an 'if' with only ':=' has no condition at all. To enter only when the new value is set, say so: 'if latest := fetchReading() then latest?'. An assignment never yields a value, so outside a head the check is its own step: 'latest := fetchReading()' then 'found := latest?'.

3. RE-POINTING A CHECKED OPTIONAL (E08040)

Inside 'if current?', writing 'if current := fetchName() then current?' (or a plain 'current := fetchName()') re-points current. 'if current?' made current.get() safe in that block - for THAT value. The assignment may put an empty Optional there. Take a NEW variable ('if replacement <- fetchName()').

The program shows each correct form. See Q1980 for the pre-flow operators, Q1981 for assigning and then checking, Q79 for :=?, Q86 for Optional safety.

Example

defines module qa.controlflow.guardfamily.misuses

  defines function

    fetchReading()
      <- rtn as Integer: 42

    fetchNothing()
      <- rtn as Integer: Integer()

    fetchName()
      <- rtn as Optional of String: Optional("Steve")

  defines program

    GuardMisusesFixed()
      stdout <- Stdout()

      //:=? only means something when the variable may be unset
      setting <- fetchNothing()
      setting :=? fetchReading()
      stdout.println(`setting=${setting}`)

      //An assignment never yields a value - assign, then check the new value
      latest <- fetchNothing()
      found <- Boolean()
      latest := fetchReading()
      found := latest?
      stdout.println(`found=${found} latest=${latest}`)

      //A ':=' head checks nothing - the explicit 'then latest?' is the gate
      if latest := fetchReading() then latest?
        stdout.println(`guarded: latest=${latest}`)

      //Inside the block a check made safe, take a NEW variable rather than re-point the checked one
      current <- fetchName()
      if current?
        if replacement <- fetchName()
          stdout.println(`replacement: ${replacement.get()}`)
        stdout.println(`current: ${current.get()}`)

Common mistakes

E08095 — 'setting' starts with a literal, so it is always set and the ':=?' below it can never assign. ':=?' is for a variable that may be unset. See ek9 -h E08095 for details.

Incorrect:

      setting <- 7

Correct:

      setting <- fetchNothing()

E07340 — ':=' in a head is a plain assignment that checks nothing, so an 'if' with only ':=' has no condition. Add 'then latest?' to enter only when the new value is set. See ek9 -h E07340 for details.

Incorrect:

      if latest := fetchReading()

Correct:

      if latest := fetchReading() then latest?

E08040 — 'if current?' made current.get() safe for the value it checked; the inner head's ':=' always assigns, so it could put an empty Optional in its place and the later current.get() would fail. Take a new variable instead. See ek9 -h E08040 for details.

Incorrect:

        if current := fetchName() then current?
          stdout.println(`replacement: ${current.get()}`)

Correct:

        if replacement <- fetchName()
          stdout.println(`replacement: ${replacement.get()}`)
Other ways to ask this
  • Which pre-flow mistakes does the EK9 compiler reject?
  • Why is a ':=' head with no condition a compile error?
  • Can I re-point an Optional inside 'if opt?'?
  • Why does ':=?' on a variable I just initialised say it can never apply?
  • What are the common mistakes with :=, :=? and <- in a control-flow head?

Coming from another language?

Kotlin: 'x = x ?: y' and 'if (y?.also { x = it } != null)' are legal and easy to get wrong; EK9 gives each intent its own operator and rejects the combinations that hide an assignment that never applies or a head that checks nothing. Java: re-assigning a variable after an isPresent() check compiles and fails at runtime with NoSuchElementException - in EK9 that is a compile error.

Keywords: compile error, E08095, Optional, <-, never applies, :=, always set, E07340, E08040, misuse, :=?, guard