How do arithmetic operators work on constrained types?

← Advanced Type System · Ref: Q716

Constrained types inherit arithmetic operators from their base type, with results returned as the constrained type.

ARITHMETIC RETURNS THE CONSTRAINED TYPE

When you add two DrivingAge values, the result is DrivingAge:

  sum <- age1 + age2

The compiler generates dual-signature operators: DrivingAge + DrivingAge and DrivingAge + Integer both work.

DUAL SIGNATURES

Every arithmetic operator gets two forms:

  result1 <- age1 + age2      //DrivingAge + DrivingAge -> DrivingAge
  result2 <- age1 + 5          //DrivingAge + Integer -> DrivingAge

This lets you work with constrained types naturally without wrapping literals.

MUTATING OPERATORS

Compound assignment and increment delegate to the base type:

  age1 += 5        //DrivingAge += Integer
  age1 -= age2     //DrivingAge -= DrivingAge
  age1++           //increment
  age1--           //decrement

MOD AND REM RETURN BASE TYPE

The mod and rem operators return the base type (Integer), not the constrained type:

  modResult <- age1 mod 7    //returns Integer, not DrivingAge
  remResult <- age1 rem 7    //returns Integer, not DrivingAge

ONLY BASE TYPE OPERATORS

A constrained type can only use operators defined on its base type. If you constrain a record that has no + operator, you cannot add constrained values.

See Q257 for constrained type overview. See Q717 for comparison operators. See Q720 for which types can be constrained.

Example

defines module qa.advancedtypes.constrainedarithmetic

  defines type

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

  defines program

    ConstrainedArithmeticDemo()
      stdout <- Stdout()

      // === ARITHMETIC: CT + CT -> CT ===
      age1 <- DrivingAge(25)
      age2 <- DrivingAge(30)

      sum <- age1 + age2
      stdout.println(`25 + 30 = ${sum}`)

      diff <- age2 - age1
      stdout.println(`30 - 25 = ${diff}`)

      // === DUAL SIGNATURES: CT + BaseType -> CT ===
      incremented <- age1 + 5
      stdout.println(`25 + 5 = ${incremented}`)

      decremented <- age2 - 10
      stdout.println(`30 - 10 = ${decremented}`)

      product <- age1 * 2
      stdout.println(`25 * 2 = ${product}`)

      // === MOD AND REM RETURN BASE TYPE (Integer) ===
      modResult <- age1 mod 7
      stdout.println(`25 mod 7 = ${modResult}`)

      remResult <- age1 rem 7
      stdout.println(`25 rem 7 = ${remResult}`)

      // === MUTATING OPERATORS ===
      mutable <- DrivingAge(20)

      mutable += 5
      stdout.println(`20 += 5 = ${mutable}`)

      mutable -= 3
      stdout.println(`25 -= 3 = ${mutable}`)

      tooYoungValue <- 10
      mutable += DrivingAge(tooYoungValue)
      stdout.println(`22 += 10 = ${mutable}`)

      // === INCREMENT AND DECREMENT ===
      counter <- DrivingAge(40)

      counter++
      stdout.println(`40++ = ${counter}`)

      counter--
      stdout.println(`41-- = ${counter}`)

Common mistakes

E50060 — DrivingAge has no intValue() method in EK9. Constrained types do not have extra methods beyond those inherited from the base type. See ek9 -h E50060 for details.

Incorrect:

sum <- age1.intValue() + age2

Correct:

sum <- age1 + age2

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

Incorrect:

incremented <- age1.intValue() + 5

Correct:

incremented <- age1 + 5
Other ways to ask this
  • Can I do math with constrained Integer types?
  • What happens when I add two constrained type values?
  • Do constrained types support increment and compound assignment?

Coming from another language?

Java: no constrained types, arithmetic on wrapper types requires unboxing. Python: no type-level constraints, arithmetic on custom classes via __add__. Rust: newtype pattern requires explicit Deref or operator trait implementations. Go: type aliases share operators but no value constraints. Kotlin: value classes wrap a single value but require manual operator definitions. EK9: constrained types automatically inherit all arithmetic operators from the base type with dual signatures (CT+CT and CT+BaseType), mutating operators delegate to base, mod/rem return base type.

Keywords: range, increment, constrained, subtract, integer, arithmetic, add, operator, mutating, compound