Why is 'name <- String()' then 'name :=? "Steve"' reported as an error (E08097)?
← Data Flow Safety · Ref: Q2254
'String()' makes a String that is present but UNSET, so a ':=?' straight after it always assigns - the 'only if unset' part can never decide anything. It is a plain assignment written as a conditional one, and a reader looks for the case where it does not assign. The compiler is sure, so it reports E08097 at the ':=?':
name <- String() name :=? "Steve" //E08097: 'name' is always unset here Stdout().println(name)
Say what the code does: declare the variable with the value ('name <- "Steve"'), or assign it with ':='. Keep ':=?' for a value that MAY already be set - one that came from a function, a parameter or a lookup.
A FALLBACK CHAIN
The usual case is the head of a chain: 'setting <- String()' then 'setting :=? fromEnvironment()' then 'setting :=? fromFile()'. Starting from 'String()' makes the first ':=?' always apply, so it is E08097. Start the chain from its first source instead - 'setting <- fromEnvironment()' then 'setting :=? fromFile()' then 'setting :=? "default"' - and every ':=?' after it genuinely decides, as the program below shows.
INSIDE 'if not x?'
There the value is unset by the condition itself, so give it a value with ':='.
NEVER REPORTED
A variable declared with NO value - 'label as String?', a returning variable '<- rtn as String?', or a property declared that way and filled in a constructor - holds no value at all until it is first written, and ':=?' is the form that fills it. In a 'pure' scope ':=' is not allowed (E08100), so ':=?' is never reported there. A guard 'if x :=? f()' also gates its block, so it is never reported either.
ONLY WHEN SURE
EK9 has no warnings, so an error must be certain. A value from a function call may be set, so the compiler stays silent; in a loop the value must be unset on every pass. The mirror is E08095: ':=?' on a target that is always SET can never apply. See Q79 for ':=?', Q1038 for fallback chains and Q2253 for E08096.
Example
#!ek9 defines module qa.dataflowsafety.assignifunsetalwaysapplies defines function //Nothing in the environment: unset fromEnvironment() <- rtn as String: String() fromFile() <- rtn as String: "from file" resolveSetting() <- rtn as String: String() setting <- fromEnvironment() setting :=? fromFile() setting :=? "default" rtn := setting retriesOrDefault() -> configured as Integer <- retries as Integer: configured if not retries? retries := 3 defines program AssignIfUnsetAlwaysAppliesDemo() stdout <- Stdout() stdout.println(`setting: ${resolveSetting()}`) //Declared with no value: ':=?' is the form that fills it label as String? label :=? "Untitled" stdout.println(`label: ${label}`) stdout.println(`retries: ${retriesOrDefault(Integer())}`) stdout.println(`kept: ${retriesOrDefault(5)}`) //Known to be unset: declare it with the value instead nickname <- "Steve" stdout.println(`nickname: ${nickname}`)
Common mistakes
E08097 — String() makes an unset String, so the first ':=?' of the chain always applies. Start the chain from its first source. See ek9 -h E08097 for details.
Incorrect:
setting <- String() setting :=? fromEnvironment() setting :=? fromFile()
Correct:
setting <- fromEnvironment() setting :=? fromFile()
E08097 — Inside 'if not retries?' the value is unset, so ':=?' always applies. Give it the value with ':='. See ek9 -h E08097 for details.
Incorrect:
retries :=? 3
Correct:
retries := 3
Other ways to ask this
- What does E08097 ':=?' always applies mean?
- Why does the compiler ask for ':=' instead of ':=?' here?
- Why is the first ':=?' of my fallback chain an error?
- When does EK9 report an assign-if-unset that always assigns?
Coming from another language?
Kotlin: x = x ?: default and C#/JavaScript: x ??= default assign only when x is null - and on a value just set to null they always assign, which no compiler reports. EK9 has no null; 'String()' is a present but UNSET String, and a ':=?' the compiler can see always applies is reported as E08097. Use ':=' or declare with the value.
Keywords: :=?, E08097, Integer(), String(), always applies, always unset, assign if unset, data flow, fallback chain, unset