E08097: assign-if-unset ':=?' always applies because the target is always unset here - it is a plain assignment; use ':=' (or declare the variable with the value)
What went wrong
An assign-if-unset ':=?' is applied to a target that is always unset at this point, so it always assigns: it is a plain assignment written as a conditional one. Data flow analysis proves the target unset here - it was given a value made by a no-argument constructor such as 'String()' or 'Integer()', or this code is inside 'if not x?' - so the "only if unset" part never decides anything.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 2254 (Why is 'name <- String()' then 'name :=? "Steve"' reported...)
- ek9 -q 1263 (If I have a String variable that might be unset, how can I...)
- ek9 -q 1264 (Walk me through using :=? on an Integer retry counter so th...)
- ek9 -q 2255 (How does the EK9 compiler decide a variable is definitely...)
Rationale
':=?' exists for a value that MAY already be set: it keeps the value if there is one and supplies a fallback if not. Used on a value that can only be unset, it says something the code does not mean, and a reader looks for the case where it does not assign. It is the mirror of E08095, where the target is always SET and ':=?' can never apply: between them, a ':=?' the compiler can decide is an error either way.
The head of a fallback chain is the usual case. 'x <- String()' then 'x :=? a()' then 'x :=? b()' starts from a value that is known to be unset, so the first ':=?' always applies. Start the chain from its first source instead - 'x <- a()' then 'x :=? b()'.
This is reported only when the compiler is SURE. EK9 has no warnings, so an error must be certain: a value that came from a function call may be set, and the compiler stays silent; inside a loop the value must be unset on every pass. Three places are never reported. A variable, returning variable or property declared with NO value ('x as String?') holds no value at all until it is first written, and ':=?' is the form that fills it - the case it exists for. In a 'pure' scope reassignment is not allowed (E08100), so ':=?' is the only way to give a value there. A guard such as 'if x :=? f()' also gates its block, which ':=' would not.
How to fix it
- Use ':=' - it says what the statement does.
- Or declare the variable with the value in the first place, instead of declaring it unset
and assigning it on the next line.
- For a fallback chain, declare the variable from the first source and keep ':=?' for the
rest.
Common causes
- 'name <- String()' then 'name :=? "default"' - write 'name <- "default"'
- A fallback chain that starts from 'String()' or 'Integer()' instead of its first source
- A returning variable declared '<- rtn as String: String()' and then filled with ':=?'
- Giving a value with ':=?' inside 'if not x?', the one place it is known to be unset
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E08097: assign-if-unset ':=?' always applies because the target is always unset here - it is a plain assignment; use ':=' (or declare the variable with the value)
#!ek9 defines module error.e08097.bad defines function //Nothing in the environment: unset fromEnvironment() <- rtn as String: String() resolveSetting() <- setting as String: String() setting :=? fromEnvironment() setting :=? "default"
Fixed: Use ':=?' only where the value may already be set
#!ek9 defines module error.e08097.good defines function //Nothing saved: unset savedName() <- rtn as String: String() //From a function call the value may be set, so ':=?' decides. nameOrDefault() <- rtn as String: savedName() rtn :=? "anonymous" //Declared with no value: ':=?' is the form that fills it. greeting() <- rtn as String? rtn :=? "Hello" //Known to be unset inside 'if not total?', so ':=' gives it the value. topUp() -> amount as Integer <- total as Integer: amount if not total? total := 0 //A guard also gates its block, which ':=' would not. printIfFound() found as String? if found :=? savedName() Stdout().println(found)
Fixed: Start the fallback chain from its first source
#!ek9 defines module error.e08097.good.fix defines function //Nothing in the environment: unset fromEnvironment() <- rtn as String: String() resolveSetting() <- setting as String: fromEnvironment() setting :=? "default"
Questions and answers
These Q&A entries show E08097 and its fix:
- Q1263: If I have a String variable that might be unset, how can I conditionally assign a default value to it only when it has not been set yet?
- Q1264: Walk me through using :=? on an Integer retry counter so that the counter only gets initialised on the first call and is preserved on subsequent calls.
- Q2254: Why is 'name <- String()' then 'name :=? "Steve"' reported as an error (E08097)?
- Q2255: How does the EK9 compiler decide a variable is definitely set or definitely unset?