How do copy, replace, and merge operators work with constrained types?

← Advanced Type System · Ref: Q719

Constrained types inherit copy (:=:), replace (:^:), and merge (:~:) operators from their base type, with both operands being the constrained type.

COPY (:=:) DELEGATES TO BASE TYPE

Copy duplicates the value from one variable to another:

  target <- DrivingAge(50)
  source <- DrivingAge(60)
  target :=: source
  //target is now 60

Both operands must be the same constrained type.

REPLACE (:^:) OVERWRITES

Replace destructively overwrites the target value:

  target :^: DrivingAge(70)
  //target is now 70

The semantics depend on the base type's replace implementation.

MERGE (:~:) COMBINES

Merge combines two values according to base type semantics. For Integer, this adds:

  target :~: DrivingAge(80)
  //for Integer: 70 + 80 = 150

For String, merge concatenates. For other types, merge follows that type's semantics.

ARGUMENT TYPE IS CONSTRAINED TYPE

These operators take the constrained type as argument, not the raw base type:

  target :=: DrivingAge(50)    //correct
  //target :=: 50              //wrong — cannot use raw Integer

See Q257 for constrained type overview. See Q241 for mutation operator details. See Q716 for arithmetic operators.

Example

defines module qa.advancedtypes.constrainedcopyreplacemerge

  defines type

    DrivingAge as Integer constrain as
      >= 16 and <= 100

  defines program

    ConstrainedCopyReplaceMergeDemo()
      stdout <- Stdout()

      // === COPY (:=:) ===
      target <- DrivingAge(50)
      source <- DrivingAge(60)

      stdout.println(`Before copy: target=${target}, source=${source}`)
      target :=: source
      stdout.println(`After copy: target=${target}`)

      // === REPLACE (:^:) ===
      target :^: DrivingAge(70)
      stdout.println(`After replace with 70: target=${target}`)

      // === MERGE (:~:) ===
      //For Integer, merge adds the values
      mergeTarget <- DrivingAge(20)
      mergeSource <- DrivingAge(30)
      mergeTarget :~: mergeSource
      stdout.println(`After merge 20+30: mergeTarget=${mergeTarget}`)

      // === COPY CONSTRUCTOR (separate from :=:) ===
      original <- DrivingAge(25)
      copied <- DrivingAge(original)
      stdout.println(`Original: ${original}, Copied: ${copied}`)

      // === ASSIGNMENT (:=) VS COPY (:=:) ===
      a <- DrivingAge(40)
      b <- DrivingAge(60)

      //Assignment replaces the variable reference
      a := b
      stdout.println(`After assignment a := b: a=${a}`)

      //Copy delegates to base type copy operator
      c <- DrivingAge(40)
      d <- DrivingAge(60)
      c :=: d
      stdout.println(`After copy c :=: d: c=${c}`)

Common mistakes

E50060 — DrivingAge has no intValue() method in EK9. DrivingAge is a constrained type, not a wrapper. See ek9 -h E50060 for details.

Incorrect:

target <- DrivingAge(50).intValue()

Correct:

target <- DrivingAge(50)

E50060 — DrivingAge has no intValue() method in EK9. Use the promote operator (#^) to extract the Integer value. See ek9 -h E50060 for details.

Incorrect:

copied <- DrivingAge(original).intValue()

Correct:

copied <- DrivingAge(original)
Other ways to ask this
  • Can I use :=:, :^:, and :~: with constrained types?
  • How do I copy values between constrained type variables?
  • What is the difference between copy, replace, and merge for constrained types?

Coming from another language?

Java: no constrained types, no copy/replace/merge operators. Manual clone() or copy constructors. Python: copy.copy() and copy.deepcopy() for object copying. Rust: Clone trait for copy, no replace/merge concept. Go: assignment copies values for value types. Kotlin: data class copy() method. EK9: constrained types inherit :=: (copy), :^: (replace), :~: (merge) from their base type — these operate between same-type constrained values, delegating to the base type's implementation.

Keywords: constrained, copy, assignment, mutating, transfer, replace, operator, merge, delegate