Why is 'count <- Integer()' then 'count++' reported as an error (E08096)?

← Data Flow Safety · Ref: Q2253

'Integer()' makes an Integer that is present but UNSET, and '++' on an unset Integer keeps it unset - the operator has no starting value to add one to, and it never invents one. So 'count++' after 'count <- Integer()' can never give 'count' a value, however often it runs. The compiler is sure of both facts, so it reports E08096 at the '++':

  count <- Integer()
  count++             //E08096: 'count' is always unset here
  stdout.println($count)

The fix is to start from a value: 'count <- 0'. The same is true of '+=', '-=', '*=' and '/=' on every built-in type that has them - start a String from "" (Q2251), a sum from 0 and a product from 1 (Q2252), a Float from 0.0, a Duration from PT0S, a Millisecond from 0ms.

INSIDE 'if not x?'
There the value is unset by the condition itself, so adding to it can do nothing. Give it a value instead, with ':=' - there ':=?' would always apply, so the compiler asks for ':=' (E08097):

  if not total?
    total := 0
  total += 1

ONLY WHEN SURE

EK9 has no warnings, so an error must be certain. E08096 fires only where the value is known to be unset - made by a no-argument constructor such as 'Integer()' or 'String()', or inside 'if not x?' - and the operator is one that is known to keep it unset. A value from a function call may be set, so the compiler stays silent; at run time an unset one is still left unset, as the program below shows. A loop is checked over every pass: a total that is only ever added to inside the loop stays unset after it, but one also assigned in the loop is left alone. Your own types define their own operators, so they are never reported.

A copy ':=:' DOES set an unset value - it is not one of these operators. See Q936 for the '?' set check and Q880 for why EK9 has no null.

Example

#!ek9
defines module qa.dataflowsafety.operatorkeepsvalueunset

  defines function

    topUp()
      -> amount as Integer
      <- total as Integer: amount
      if not total?
        total := 0
      total += 1

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

  defines program

    OperatorKeepsValueUnsetDemo()
      stdout <- Stdout()

      count <- 0
      count++
      count++
      stdout.println(`count: ${count}`)

      stdout.println(`topped up: ${topUp(Integer())}`)
      stdout.println(`kept: ${topUp(5)}`)

      //From a function call the compiler cannot be sure, so it is silent - and at run time it stays unset
      supplied <- unsetSupply()
      supplied += 1
      stdout.println(`supplied unset, after +=: set=${supplied?}`)

      elapsed <- PT0S
      elapsed += PT1H30M
      stdout.println(`elapsed: ${elapsed}`)

      waited <- 0ms
      waited += 750ms
      stdout.println(`waited: ${waited}`)

Common mistakes

E08096 — Integer() makes an unset Integer and '++' keeps it unset, so the count never gets a value. Start from 0. See ek9 -h E08096 for details.

Incorrect:

count <- Integer()

Correct:

count <- 0

E08096 — Inside 'if not total?' the value is unset, and '+=' keeps it unset. Give it a value with ':='. See ek9 -h E08096 for details.

Incorrect:

      total += 0

Correct:

      total := 0
Other ways to ask this
  • What does E08096 'always unset here' mean?
  • Why can '+=' or '++' never give an unset value a value?
  • Why does the compiler stop me adding to an unset Integer?
  • When does EK9 report an operator that keeps a value unset?

Coming from another language?

Java: Integer count = null; count++ throws NullPointerException at run time. Python: count = None; count += 1 raises TypeError. EK9: 'count <- Integer()' is a present but UNSET Integer; '++' keeps it unset, which would fail silently, so the compiler reports E08096 when it is sure. Start from 0.

Keywords: ++, +=, :=?, E08096, Integer(), String(), always unset, data flow, keeps unset, tri-state, unset