How do I extract the base type value from a constrained type?

← Advanced Type System · Ref: Q718

The promote operator (#^) extracts the base type value from a constrained type, providing an explicit escape hatch for crossing type boundaries.

PROMOTE RETURNS THE BASE TYPE

The #^ operator extracts the underlying value:

  rawInt <- #^ age        //DrivingAge -> Integer

The result is the base type, not the constrained type. Only types whose base type defines promote (#^) support this operator.

WHICH CONSTRAINED TYPES HAVE PROMOTE

Promote is available when the base type defines it. Integer defines promote (to Float), so DrivingAge has #^. String does NOT define promote, so a String-constrained type like Name cannot use #^. Use $ (string conversion) instead for String-constrained types.

CONSTRAINED ENUMS BLOCK PROMOTE

Constrained enumerations deliberately prevent promotion back to the base enum:

  redSuit <- RedCardSuit("Hearts")
  //Cannot do: cardSuit <- #^ redSuit   //No promote to CardSuit

This is by design: constrained enums are fully disconnected types. If you could promote RedCardSuit to CardSuit, it would bypass the type boundary.

WHY PROMOTE EXISTS

Constrained types are disconnected: DrivingAge is NOT an Integer subtype. You cannot pass DrivingAge where Integer is expected. Promote is the explicit conversion:

  processInteger()
    -> val as Integer
    //...
  age <- DrivingAge(25)
  rawInt <- #^ age
  processInteger(rawInt)     //extract then pass

PROMOTE VS STRING

Two different conversions:

  text <- $age          //String representation: "25"
  raw <- #^ age         //Base type value: Integer 25

Both are useful but serve different purposes.

See Q257 for constrained type overview. See Q25 for promote operator basics. See Q722 for type hierarchy details.

Example

#!ek9
defines module qa.advancedtypes.constrainedpromote

  defines type

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

    Name as String constrain as
      matches /^[a-zA-Z -]+$/

  defines function

    <?-
      Accepts an Integer — cannot take DrivingAge directly.
    -?>
    processInteger() as pure
      -> intValue as Integer
      <- result as String: `Raw integer: ${intValue}`

  defines program

    ConstrainedPromoteDemo()
      stdout <- Stdout()

      age <- DrivingAge(25)

      // === PROMOTE TO BASE TYPE ===
      rawInt <- #^ age
      stdout.println(`Promoted DrivingAge to Integer: ${rawInt}`)

      // === PASS TO FUNCTION EXPECTING BASE TYPE ===
      extracted <- #^ age
      output <- processInteger(extracted)
      stdout.println(output)

      // === STRING CONVERSION VS PROMOTE ===
      asText <- $age
      stdout.println(`String conversion: ${asText}`)

      asInteger <- #^ age
      stdout.println(`Promote to Integer: ${asInteger}`)

      // === STRING-CONSTRAINED: use $ not #^ ===
      //String has no promote operator, so Name does not either.
      //Use string conversion ($) instead:
      name <- Name("Alice Smith")
      nameAsStr <- $name
      stdout.println(`Name as String: ${nameAsStr}`)

      // === ISSET STILL WORKS ===
      isValid <- age?
      stdout.println(`DrivingAge is set: ${isValid}`)

      // === HASHCODE ===
      hashVal <- #? age
      stdout.println(`Hashcode: ${hashVal}`)

Common mistakes

E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.

Incorrect:

output <- processInteger(extracted).toUpperCase()

Correct:

output <- processInteger(extracted)

E50060 — Name has no getValue() method in EK9. Use the $ prefix operator to convert to String. See ek9 -h E50060 for details.

Incorrect:

nameAsStr <- name.getValue()

Correct:

nameAsStr <- $name
Other ways to ask this
  • What does the #^ promote operator do with constrained types?
  • How do I convert a DrivingAge back to an Integer?
  • Why can't I promote a constrained enumeration to its base enum?

Coming from another language?

Java: no constrained types, no promote concept. Explicit casting or wrapper.getValue() for custom wrappers. Python: no type constraints, no promote. Rust: newtype pattern uses .0 field access or Into trait for conversion. Go: type conversion syntax T(value) converts between compatible types. Kotlin: value classes use underlying property access. EK9: promote (#^) is the explicit operator for crossing constrained type boundaries — returns the base type value, available on value-constrained types but blocked on constrained enumerations.

Keywords: base-type, boundary, constrained, convert, disconnected, enum, explicit, extract, promote