tur/range-bound
Bound: typed range endpoints (R3).
range.tur encodes a range endpoint's kind as a sentinel int -- the inclusive : int argument to range-bound-new (1 = inclusive, 0 = exclusive) plus the null pointer (0) standing for "unbounded". That is a classic stringly-typed sum. This module replaces it with a real algebraic type, Bound:
Inclusive v -- the endpoint v is part of the interval Exclusive v -- the endpoint v is the open boundary Unbounded -- no endpoint on this side
As of range-gadt-typeclass-migration-plan B1 Bound IS range.tur's internal endpoint representation (defined in range.tur, re-exported here), so this module is now a thin convenience layer. The historical compatibility shims (bound->range-bound and range-bound->bound) survive one release as near-identity functions for source compatibility; new code can use the constructors and predicates directly.
Bound is declared :copy, so its endpoints are plain value types that can be read repeatedly (a shared range bound is destructured by many predicates); see docs/archive/history/defgadt-copy-and-shared-bounds.md. It was a defgadt whose every constructor returned (Bound int); it is an ordinary defdata with no type parameter now, and goes by value (adt-gadt-unification-plan slice 3).
Since: stdlib-refinement-collections-plan (R3)
bound-inclusive
(bound-inclusive [v :int] :Bound)
construct an inclusive endpoint at v.
| v | the endpoint value (part of the interval) |
A Bound tagged Inclusive.
(range-contains? (bound-range (bound-inclusive 1) (bound-unbounded)) 1) ; => true
Since: R3
bound-exclusive
(bound-exclusive [v :int] :Bound)
construct an exclusive endpoint at v.
| v | the open boundary value (not part of the interval) |
A Bound tagged Exclusive.
(range-contains? (bound-range (bound-inclusive 1) (bound-exclusive 5)) 5) ; => false
Since: R3
bound-unbounded
(bound-unbounded :Bound)
construct an unbounded (open-ended) endpoint.
A Bound tagged Unbounded.
(range-contains? (bound-range (bound-unbounded) (bound-unbounded)) 999) ; => true
Since: R3
bound->range-bound
(bound->range-bound [b :Bound] :Bound)
deprecated compatibility shim (now near-identity).
Before range-gadt-typeclass-migration-plan B1 this lowered a Bound to range.tur's pointer representation. Now that range.tur's internal endpoint representation IS Bound, it is the identity. Retained one release for source compatibility; prefer passing the Bound directly to range-new.
| b | the Bound |
The same Bound, accepted by range-new and every range.tur predicate.
(range-bound-inclusive? (bound->range-bound (bound-inclusive 3))) ; => true
Since: R3
range-bound->bound
(range-bound->bound [b :Bound] :Bound)
deprecated compatibility shim (now near-identity).
Before range-gadt-typeclass-migration-plan B1 this lifted range.tur's pointer representation into Bound. Now that range.tur's endpoints ARE Bound values, it is the identity. Retained one release for source compatibility; prefer using range-lower / range-upper directly.
| b | a Bound |
The same Bound.
(bound-value (range-bound->bound (range-lower (closed-range 2 8)))) ; => 2
Since: R3
bound-value
(bound-value [b :Bound] :int)
extract the endpoint value from a bounded endpoint.
| b | a Bound; Unbounded yields 0 |
The endpoint value for Inclusive / Exclusive, or 0 for Unbounded.
(bound-value (bound-exclusive 7)) ; => 7
Since: R3
bound-inclusive?
(bound-inclusive? [b :Bound] :bool)
true when an endpoint is inclusive.
| b | a Bound |
true for Inclusive, false for Exclusive or Unbounded.
(bound-inclusive? (bound-inclusive 1)) ; => true
Since: R3
bound-range
(bound-range [lo :Bound hi :Bound] :Range)
build a Range from two Bound endpoints.
The Bound-typed analogue of range.tur's positional constructors (closed-range, open-range, ...): pick any Inclusive / Exclusive / Unbounded combination for each side.
| lo | the lower Bound | |
| hi | the upper Bound |
A Range, usable with every range.tur predicate.
(range-contains? (bound-range (bound-inclusive 1) (bound-exclusive 5)) 4) ; => true
Since: R3
range-lower-bound
(range-lower-bound [r :Range] :Bound)
the lower endpoint of a Range as a Bound.
| r | a Range |
The lower endpoint as a Bound.
(bound-inclusive? (range-lower-bound (closed-range 1 5))) ; => true
Since: R3
range-upper-bound
(range-upper-bound [r :Range] :Bound)
the upper endpoint of a Range as a Bound.
| r | a Range |
The upper endpoint as a Bound.
(bound-unbounded? (range-upper-bound (at-least-range 3))) ; => true
Since: R3
range-fmt
(range-fmt [lo-kind :int lo-val :int hi-kind :int hi-val :int] :cstr)
bound-kind
(bound-kind [b :Bound] :int)
range->str
(range->str [r :Range] :cstr)
render a Range in interval notation.
Renders the endpoints with bracket/paren according to inclusivity, e.g. "[1, 5)" for a closed-open range or "(-inf, 5]" for an at-most range.
| r | a Range |
A freshly allocated :cstr interval string.
(range->str (closed-open-range 1 5)) ; => "[1, 5)" (range->str (at-most-range 5)) ; => "(-inf, 5]"
Since: range-gadt-typeclass-migration-plan B2
bound->str
(bound->str [b :Bound] :cstr)
render a single Bound endpoint.
Renders "[v" for Inclusive, "(v" for Exclusive, or "unbounded". A lone endpoint has no side, so the inclusive/exclusive bracket is the lower-side form; use range->str for a full interval.
| b | a Bound |
A freshly allocated :cstr.
(bound->str (bound-inclusive 3)) ; => "[3" (bound->str (bound-unbounded)) ; => "unbounded"
Since: range-gadt-typeclass-migration-plan B2
bound-fmt
(bound-fmt [kind :int v :int] :cstr)