What does the merge operator ':~:' do to a value that is already set?

← Operators and Expressions · Ref: Q1405

Nothing. ':~:' never overwrites a set value; it fills whatever is unset or missing, element by element, recursing into compatible nested structures; Lists append, Sets take the union. The first value recorded wins.

SINGLE VALUES

For a single value (Integer, Float, String, Boolean, Character, Date, Time, Money, Optional and the rest) that means: the target takes the source ONLY when the target is unset and the source is set.

  count <- Integer()
  count :~: 5
  count :~: 7
  stdout.println($count)

The first merge fills the unset count with 5; the second is ignored because count is now set.

THE FOUR CASES

- set target, set source: the target is kept
- set target, unset source: the target is kept
- unset target, set source: the target takes the source
- unset target, unset source: the target stays unset
Money follows the same rule whatever the currency: a set 10.00#GBP merged with 5.00#USD stays 10.00#GBP.

TO OVERWRITE

Use ':=:' (copy) or ':^:' (replace) when you mean to replace a set value.

NOT THE SAME AS A STREAM

Streaming into a value with '>' is a fold, not a merge, so 'cat [1, 2, 3] > total' still sums to 6.

See Q1406 for merging Dicts gathered from several tasks. See Q1409 for Lists and Sets. See Q1410 for writing a merge of your own. See Q1058 for copy, replace and merge on records.

Example

defines module qa.operators.mergerulesinglevalues

  defines program

    MergeRuleSingleValuesDemo()
      stdout <- Stdout()

      //A set target is never overwritten
      kept <- 10
      kept :~: 20
      stdout.println(`integer set kept: ${kept}`)

      //An unset target takes a set source
      filled <- Integer()
      filled :~: 30
      stdout.println(`integer unset filled: ${filled}`)

      text <- "original"
      text :~: "replacement"
      stdout.println(`string set kept: ${text}`)

      noText <- String()
      noText :~: "supplied"
      stdout.println(`string unset filled: ${noText}`)

      //Money keeps a set value whatever the source currency
      price <- 10.00#GBP
      price :~: 5.00#USD
      stdout.println(`money other currency kept: ${price}`)

      //The first value recorded wins
      first <- Integer()
      second <- 5
      first :~: second
      first :~: 7
      stdout.println(`first recorded wins: ${first}`)

      //Streaming into a value is a fold, not a merge
      total <- Integer()
      cat [1, 2, 3] > total
      stdout.println(`pipe still sums: ${total}`)

Common mistakes

E08080 — Merging a variable into itself can never change it, so the compiler reports a self assignment. Merge a different value in. See ek9 -h E08080 for details.

Incorrect:

      first :~: first

Correct:

      first :~: second
Other ways to ask this
  • Does ':~:' overwrite an Integer or String that already has a value?
  • Why did merging 5 into 10 leave 10?
  • When does merge take the source value for a single value?
  • Is merge the same as copy for a Date or Money?

Coming from another language?

Java: there is no merge operator; Map.putIfAbsent and Optional.or are the nearest single-purpose equivalents. Kotlin: '?:' gives a default for a null, but does not mutate. Python: dict.setdefault for one key. EK9: one operator, ':~:', with one rule on every built-in type - a set value is never overwritten, an unset one is filled.

Keywords: E08080, unset target, Money currency, fill, merge, single value, :~:, first recorded wins, overwrite