What are the correct patterns for boolean conditions and comparisons?

← Comparison Patterns · Ref: Q689

EK9 enforces clean boolean condition patterns. Conditions must use variables (not literals) and comparisons must have matching types. A ternary expression is typed to a type its branches share: the same type, one branch promoting to the other's type, or their nearest common super or trait - never an invented Any.

NO BOOLEAN LITERALS IN CONDITIONS (E07390)

Using 'true' or 'false' directly in a condition is flagged as a pointless constant:

  if true                 // E07390: always true, dead code
    stdout.println("always")

The correct form tests a boolean variable:

  if isValid
    stdout.println("valid")

TYPE MATCHING IN COMPARISONS (E07620)

Both sides of a comparison must be compatible types:

  if count > 0
    stdout.println(count)

This does not compile:

  name <- "Steve"
  if name == 42          // E07620: String has no == for Integer
    stdout.println(name)

TERNARY TYPES

The result is one of the branches, so it must have a type both branches have. The same type or a subtype gives that type; a branch that promotes to the other's type (Integer to Float) gives the other's type; otherwise the nearest common super class or trait. Branches that share nothing but Any are a compile error:

  age <- 20
  label <- age >= 18 <- "adult" else 0     // ERROR E50030: String and Integer share only Any

Give both branches one type:

  label <- isAdult <- "adult" else "minor"

VARIABLE-BASED CONDITIONS

Store intermediate boolean results in named variables:

  isAdult <- age >= adultThreshold
  isVerified <- account.verified()
  if isAdult and isVerified
    grantAccess()

See Q637 for comparison pattern basics. See Q639 for boolean parameter patterns. See Q641 for capture requirements.

Example

defines module qa.comparison.booleanconditions

  defines constant

    ADULT_AGE_THRESHOLD <- 18
    SENIOR_AGE_THRESHOLD <- 65
    MINIMUM_BALANCE <- 100.0

  defines function

    <?-
      Correct: boolean conditions use variables, not literals.
      Named intermediates make intent clear.
    -?>
    classifyCustomer() as pure
      ->
        customerAge as Integer
        accountBalance as Float
      <- category as String: "standard"

      isAdult <- customerAge >= ADULT_AGE_THRESHOLD
      isSenior <- customerAge >= SENIOR_AGE_THRESHOLD
      hasMinBalance <- accountBalance >= MINIMUM_BALANCE

      if isSenior and hasMinBalance
        category: "premium senior"
      else if isAdult and hasMinBalance
        category: "premium"
      else if isAdult
        category: "standard"
      else
        category: "junior"

    <?-
      Correct: comparisons use matching types.
      Integer compared with Integer, String with String.
    -?>
    compareItems() as pure
      ->
        itemName as String
        itemCount as Integer
        targetName as String
        targetCount as Integer
      <- isMatch as Boolean: false

      nameMatches <- itemName == targetName
      countSufficient <- itemCount >= targetCount

      isMatch: nameMatches and countSufficient

  defines program

    BooleanConditionPatternsDemo()
      stdout <- Stdout()

      stdout.println(classifyCustomer(70, 500.0))
      stdout.println(classifyCustomer(25, 50.0))
      stdout.println(classifyCustomer(15, 200.0))

      matched <- compareItems(itemName: "widget", itemCount: 10, targetName: "widget", targetCount: 5)
      stdout.println(`Match: ${matched}`)

Common mistakes

E07620 — Both sides of a comparison must be compatible types. Comparing String with Integer is a type mismatch. See ek9 -h E07620 for details.

Incorrect:

nameMatches <- itemName == targetCount

Correct:

nameMatches <- itemName == targetName

E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.

Incorrect:

classifyCustomer(70, 500.0).toUpperCase()

Correct:

classifyCustomer(70, 500.0)

E50060 — Boolean has no toString() method in EK9; use the $ prefix operator (here via ${...} interpolation) to convert to String. See ek9 -h E50060 for details.

Incorrect:

stdout.println(matched.toString())

Correct:

stdout.println(`Match: ${matched}`)
Other ways to ask this
  • What is E07620 type mismatch in comparison?
  • What is E07390 boolean literal in condition?
  • What type does a ternary expression produce when the branches differ?
  • How should boolean conditions be written in EK9?

Coming from another language?

Java: boolean literals in conditions are valid (just warnings from linters). Python: no type checking for comparisons. C: any value in condition. Rust: condition must be bool. EK9: conditions must be boolean variables or expressions, no bare literals, types must match.

Keywords: type, condition, E07390, E07620, ternary, literal, boolean, pattern, comparison