Define a Percentage type constrained to values between 0 and 100.
← Advanced Type System · Ref: Q1185
Use 'constrain as' with range checks:
defines type Percentage as Integer constrain as >= 0 and <= 100
A bare constructor ASSERTS validity: Percentage(150) Panics at runtime on an out-of-range value, and a violating literal constant is the compile error E08260. For untrusted input use the fallible factory Percentage().of(value), which returns an UNSET object on violation (never Panics) — check with '?' after construction. Constrained types are disconnected from their base type — Percentage is NOT an Integer subtype.
See Q257 for constrained types. See Q1074 for constrained in record. See Q720 for constrainable types.
Example
defines module qa.advancedtypes.constrainedtype defines type Percentage as Integer constrain as >= 0 and <= 100 defines record ExamScore studentName as String: String() score as Percentage: Percentage(0) ExamScore() -> studentName as String score as Percentage this.studentName :=: studentName this.score :=: score default operator defines program ConstrainedTypeDemo() stdout <- Stdout() //Valid — within range passing <- Percentage(85) stdout.println(`Valid score set: ${passing?}`) if passing? stdout.println(`Score: ${passing}`) //Invalid — above 100: the fallible factory returns unset (a bare Percentage(150) would Panic at //runtime, and a violating literal is the compile error E08260) tooHigh <- Percentage().of(150) stdout.println(`Too high set: ${tooHigh?}`) //Invalid — below 0: of() returns unset negative <- Percentage().of(-5) stdout.println(`Negative set: ${negative?}`) //Boundary values zero <- Percentage(0) hundred <- Percentage(100) stdout.println(`Zero set: ${zero?}`) stdout.println(`Hundred set: ${hundred?}`) //Use in a record result <- ExamScore("Alice", Percentage(92)) stdout.println(`${result}`)
Common mistakes
E50060 — Constrained types are disconnected from their base type, so passing the Integer 92 where a Percentage is expected fails to resolve the ExamScore constructor — wrap it as Percentage(92). See ek9 -h E50060 for details.
Incorrect:
ExamScore("Alice", 92)
Correct:
ExamScore("Alice", Percentage(92))
Other ways to ask this
- Write code to create a constrained numeric type that rejects out-of-range values
- I need a type that only accepts integers from 0 to 100 for percentage values
- In Rust I'd use a newtype with validation. Write the EK9 constrained type equivalent
- Build a Percentage constrained type and show how invalid values become unset
Coming from another language?
Java: no built-in, uses Bean Validation @Min/@Max. Python: no type-level constraints. Rust: newtype with manual validation. Kotlin: value class with init check. EK9: 'constrain as' creates a validated, disconnected type.
Keywords: constrain as, validation, constrained, type, range, disconnected, percentage