Does 'if allowed <- permitted()' run its body when permitted() returns false?

← Control Flow · Ref: Q2001

No. A guard normally enters when its value is SET, but over a Boolean an 'if' (or 'else if') guard enters only when the value is SET AND TRUE. 'if allowed <- permitted(user)' skips the body for false just as it does for an unset result, so no trailing 'then allowed' is needed. The same holds for ':=?' in an if head: 'if decision :=? permitted(false)' fills decision with false and skips the body.

It is deliberately narrow:
- only 'if' and 'else if'; a 'switch' keeps enter-if-set, so its 'case false' stays reachable, and so do 'while', 'do', 'for' and 'try'
- only '<-' and ':=?'; a ':=' head is blind (E07340 without a condition)
- only a variable DECLARED Boolean; a type that merely promotes to Boolean keeps enter-if-set

An unset Boolean skips the body as any unset value does.

See Q74 for the guard in an if and Q1980 for the head forms side by side.

Example

#!ek9
defines module qa.controlflow.booleantruthguard

  defines function

    permitted()
      -> allowed as Boolean
      <- rtn as Boolean: allowed

    unknown()
      <- rtn as Boolean: Boolean()

  defines program

    BooleanTruthGuard()
      stdout <- Stdout()

      if granted <- permitted(true)
        stdout.println(`if over true: entered, granted=${granted}`)

      if refused <- permitted(false)
        stdout.println(`if over false: entered, refused=${refused}`)
      else
        stdout.println("if over false: skipped")

      if missing <- unknown()
        stdout.println(`if over unset: entered, missing=${missing}`)
      else
        stdout.println("if over unset: skipped")

      decision <- Boolean()
      if decision :=? permitted(false)
        stdout.println(`fill with false: entered, decision=${decision}`)
      else
        stdout.println(`fill with false: skipped, decision set=${decision?}`)

      switch answer <- permitted(false)
        case true
          stdout.println("switch over false: case true")
        case false
          stdout.println(`switch over false: case false, answer=${answer}`)
        default
          stdout.println("switch over false: default")

Common mistakes

E07340 — ':=' in a head is a blind assignment and checks nothing, so the if has no condition. Use ':=?' (which fills an unset target and, over a Boolean in an if, enters only when true) or add 'then decision'. See ek9 -h E07340 for details.

Incorrect:

      if decision := permitted(false)

Correct:

      if decision :=? permitted(false)
Other ways to ask this
  • How does a guard behave when the value is a Boolean?
  • Why is my if guard skipped when the Boolean is false?
  • Does a switch guard over a Boolean still reach case false?
  • Is 'then allowed' needed after 'if allowed <- check()'?

Coming from another language?

Swift: 'if let allowed = permitted()' enters for false (it only unwraps). Kotlin: 'permitted()?.let { }' enters for false. Java: 'if (Boolean.TRUE.equals(permitted()))' is the null-safe truth test. EK9: the guard over a Boolean is that null-safe truth test, in an if.

Keywords: Boolean, assign-if-unset, case false, declaration guard, else if, guard, if, switch, truth