How do I use guarded assignment :=? on an external object field?
← Control Flow · Ref: Q760
The guarded assignment operator (:=?) works on external object fields accessed via dot notation. It checks whether the field is currently SET, and only assigns if the field is UNSET. This is useful for providing default values to record or class fields without overwriting existing values.
BASIC EXTERNAL FIELD GUARD
Assign default values to record fields:
config <- Config() config.host :=? "localhost" config.port :=? 8080
If host and port are unset (default-constructed), the defaults are applied.
PRESERVING EXISTING VALUES
If a field already has a value, the guarded assignment is skipped:
config <- Config() config.host :=? "localhost" config.host :=? "should-not-overwrite"
After both lines, config.host is still "localhost" because the second :=? finds the field already set.
CONFIGURATION PATTERN
A common use is applying layered defaults to a configuration object:
config <- loadFromFile() config.host :=? loadFromEnv("HOST") config.port :=? loadFromEnv("PORT") config.host :=? "localhost" config.port :=? 8080
File values win, then environment, then hardcoded defaults.
DIFFERENCE FROM LOCAL VARIABLE :=?
The semantics are identical — assign only when unset. The difference is that the compiler generates field access (getfield/putfield) rather than local variable access. Both check the isSet state of the target.
See Q79 for guarded assignment on local variables. See Q22 for variable declaration. See Q104 for uninitialised properties in classes.
Example
defines module qa.flow.guard.external.field defines record Config host <- String() port <- Integer() default operator ? defines program GuardedExternalFieldDemo() stdout <- Stdout() // === BASIC EXTERNAL FIELD GUARD === config <- Config() config.host :=? "localhost" config.port :=? 8080 stdout.println(config.host) stdout.println(config.port) // === PRESERVING EXISTING VALUES === config.host :=? "should-not-overwrite" config.port :=? 9090 stdout.println(config.host) stdout.println(config.port) stdout.println("Done")
Common mistakes
E50030 — The guarded assignment operator :=? checks the target field's isSet state and conditionally assigns. The assigned value must be type-compatible with the field. Assigning an Integer to a String field (or vice versa) triggers E50030. See ek9 -h E50030 for details.
Incorrect:
config.host :=? 42 config.port :=? "not a number"
Correct:
config.host :=? "localhost" config.port :=? 8080
Other ways to ask this
- Can I use :=? on a record field via dot access?
- How does guarded assignment work on config.host?
- What does object.field :=? value do in EK9?
Coming from another language?
Java: No direct equivalent. Must write: 'if (config.getHost() == null) { config.setHost("localhost"); }'. Requires getter/setter boilerplate and manual null check. Kotlin: 'config.host = config.host ?: "localhost"'. Elvis operator handles null but requires reading the field twice. Go: 'if config.Host == "" { config.Host = "localhost" }'. Must know the zero value for each type. EK9: 'config.host :=? "localhost"' is a single operator that checks the field's isSet state and conditionally assigns. Works uniformly across all types.
Keywords: access, operator, conditional, guarded, dot, unset, external, assignment, safe, record, property, isset, field, object, default, config