Range API

Brian Goetz brian.goetz at oracle.com
Mon Sep 23 18:47:49 UTC 2024


This is a nice start.  I'd like to add a few comments to help set 
expectations and to guide future explorations here.

Starting with a "main goal" of "putting it in the JDK" is putting the 
cart before the horse.  The main goal should be to solve the problem 
well enough, for a broad enough variety of uses, that it might be 
_considered as a candidate for the JDK_ (possibly with some additional 
work), but it should stand well enough on its own.  (The Joda Time -> 
java.util.time transition is a good example of doing this right.)  Build 
a good thing first, then let's have a conversation about whether it 
belongs in the JDK.

Another consideration I'd like to put on the table is: can it be a value 
type?  Because an immutable range (and we wouldn't consider anything 
else) is an ideal candidate for being a value.  (This is one of many "is 
it going where the language is going" questions; I'll keep the rest of 
those to myself for now.)

As has been pointed out already, one would prefer to use a Comparator as 
a witness to ordering rather than the much weaker Comparable.

I would want to see Range implement Iterable (yes, I understand this 
rules out some possible types of ranges), as iterating over the elements 
of a range is one of the most likely operations.

I think the algebraic operations as you've proposed them (union, 
intersection, etc) are a bit of a mess, union especially.





On 9/22/2024 3:01 PM, Olexandr Rotan wrote:
> Hello everyone! I am writing here today to invite everyone to 
> participate in the discussion regarding the Range APi proposal I have 
> made into JDK. Here is the pull request link: 
> https://github.com/openjdk/jdk/pull/21122, and PR text:
>
> This pull request describes the methods of the |Range<T>| interface. 
> The |Range<T>| interface represents a bounded or unbounded range. 
> (From now on, range, span and interval are used interchangeably, but 
> docs only use "range")
>
>
>       Goals:
>
>  *
>
>     *Main goal. Standardization of the Range/Interval API:* The
>     primary objective of this effort is to provide a standardized
>     interface for working with ranges or spans of time (or any ordered
>     types). Many existing libraries offer their own custom
>     implementations of ranges, and they differ in significant ways,
>     making it harder to use and combine across different codebases.
>     Standardization will ensure consistency, interoperability, and a
>     more predictable interface across various contexts.
>
>  *
>
>     *Versatile range operations:* provide a comprehensive API for
>     manipulating and querying ranges, especially those representing
>     time periods or numerical intervals. The API simplifies common
>     tasks like checking containment, overlaps, or adjacency between
>     ranges.
>
>  *
>
>     *Support for unbounded ranges:* Unlike many existing libraries,
>     which assume intervals are always bounded, this API aims to fully
>     support unbounded intervals. Users will be able to define ranges
>     with open starts or ends, making it suitable for temporal data
>     that spans indefinitely in one direction, such as future
>     projections or historical data with unknown starting points.
>
>  *
>
>     *Performance efficiency:* The API aims to provide optimized for
>     performance implementation, that takes advantage of all possible
>     simplifications and short-circuits.
>
>  *
>
>     *Consistency with existing libraries:* To aid adoption, the API
>     should be familiar to developers who have used popular libraries
>     like NodaTime, Joda-Time, Three-Ten Extra, and Boost Date_Time,
>     but with enhancements for unbounded intervals, negative ranges
>     (?), and optional return types instead of null values.
>
>
>       Non-Goals:
>
>  *
>
>     *Handling complex data structures beyond smple ranges:* This API
>     is not intended to manage or represent complex data structures
>     beyond ranges. For example, ranges that involve intricate internal
>     states, like non-contiguous ranges (?) or ranges with multiple
>     gaps, are out of scope.
>
>  *
>
>     *Overly simplifying range types:* While ease of use is a goal, its
>     is not an aim to remove support for advanced cases like unbounded
>     or negative ranges, even if this results in slightly more complex
>     implementations. The API should not be skewed towards being purely
>     a simple data structure for bounded ranges.
>
>  *
>
>     *Application-specific logic:* The API is meant to be
>     domain-agnostic and general-purpose. It is not intended to allow
>     to embed application-specific logic, such as calendar-based date
>     manipulations or domain-specific business rules for interval
>     comparison.
>
>  *
>
>     *Replacing existing libraries:* The goal is not to replace
>     established libraries like Joda-Time or ThreeTen-Extra, but rather
>     to augment these ideas with support for unbounded ranges and
>     additional arithmetic operations. Although, it is a goal to
>     provide interface that could exisiting libraries could easily
>     retrofit into.
>
>
>       Motivation
>
> The primary motivation behind standardizing the |Range| API is the 
> *lack of an established, universal interface* for handling ranges or 
> spans across various domains. Developers are often forced to work with 
> different, incompatible range implementations across libraries or to 
> re-implement common functionality themselves. This leads to redundant 
> code, increased dependencies, and greater chances for errors.
>
> In many software systems—whether in scheduling, auditing, access 
> control, or financial services—ranges are used to represent periods of 
> time, numerical intervals, or validity spans. Without a standardized 
> API, developers must contend with diverse implementations that often 
> differ in naming conventions, behavior, and supported features. These 
> variations create unnecessary complexity, as developers must:
>
> 1.
>
>     *Introduce additional dependencies*: Many libraries provide
>     similar functionality for ranges, but since they are not
>     interchangeable, developers must often add extra dependencies to
>     cover edge cases or specific use cases that are not available in a
>     single library. This bloats the codebase and creates maintenance
>     overhead.
>
> 2.
>
>     *Re-implement common logic*: In cases where no single library
>     meets the required needs, developers are forced to write their own
>     range-handling logic. This reinvention of basic operations such as
>     intersection, union, or containment leads to redundancy, increased
>     likelihood of bugs, and inconsistency in how ranges are handled
>     across different parts of the code.
>
> 3.
>
>     *Fragmentation across domains*: Different libraries often define
>     their own range concepts (e.g., for date-times, numbers, or
>     general comparisons), which are rarely compatible with one
>     another. This lack of compatibility makes integration between
>     systems difficult, requiring custom adapters or conversions.
>
> By defining a standard |Range| API, the goal is to:
>
>   * *Reduce the dependency footprint:* A common, well-designed API for
>     ranges would eliminate the need to import multiple libraries just
>     to handle different types of ranges, reducing dependencies in
>     projects and enhancing maintainability.
>   * *Simplify code and increase reusability:* With a standardized
>     interface, developers can write range-related code once and reuse
>     it across projects and libraries, confident that the same
>     semantics and operations will apply consistently.
>   * *Minimize developer errors:* By providing a predictable and
>     well-documented interface, the likelihood of misunderstandings or
>     incorrect use of range operations will decrease. Developers can
>     trust that operations like intersections, unions, and comparisons
>     will behave consistently, regardless of the context.
>
> In essence, the lack of standardization in range operations creates 
> unnecessary complexity, fragmentation, and redundant effort. A 
> standardized |Range| API would provide clarity, reduce the need for 
> additional dependencies, and enable more efficient, reusable, and 
> error-free code across different projects and domains.
>
>
>   Key API points
>
>
>     Support of unbounded intervals
>
> API supports both one- and two-sided. Provided sample (draft) 
> implementation for|ChronoDateTime| has 4 separate implementation for 
> each type of ranges.
>
>
>       Alternatives
>
>   * Many Libraries, like Luxon, C++ boost, NodaTime and many others,
>     arguably the most, fo not explicitly support unbounded intervals.
>     This reduces complexity of implementation, but takes away many
>     possible optimization for edge-cases. Alternative they propose is
>     to use Instant.MIN and Instant.MAX or similar to create
>     unbounded-like intervals.
>
>
>     Support for negative intervals,
>
> API supports both positive and negative and positive ranges. This is 
> questionable and discussion is encouraged.
>
>
>       Advantages
>
>   * Allows more flexible usage of API, which would be helpful for use
>     cases like diagrams visualization.
>
>
>       Disadvantages
>
>   * Dramatically increases amount of boilerplate code inside the
>     implementations.
>   * Makes behaviour of potential methods like |boolean endsBefore(T t)
>     |unintuitive. Does this mean that end() is before that provided
>     parameter, or latter of bounds (i.e. |start()| for negative range
>     and |end()| for positive).
>   * Limited usability scope. Most use cases would not benefit from
>     possibility of negative ranges creation, but would have to suffer
>     performance decrease.
>
> In general, either there should be support for negative ranges, or 
> ranges might be end-exclusve, but not two at the same time, as having 
> them both together dramatically increases complexity.
>
>
>     |Range| is not |Serializable|
>
> Currently ranges are not|Serializable|. This is due to difficulties 
> regarding using non-serializable interfaces, like |ChronoDateTIme | in 
> sample implementation.
>
>
>       Alternatives
>
>   * Restrict range type variable to implement |Serializable|. I see
>     this option as undesiarable bacause of how much it narrows use of
>     interface.
>
>
>     Current interface methods list is minimal
>
> For now, API proposed contains minimal amount of methods that are used 
> in range arithmetics. List of methods is supposed to change as 
> discussion moves on.
>
>
>     Generic Range class vs Rnage interface + specific inmplementations
>
> Currently, approach is to define interface and list of implementations.
>
>
>       Advantages
>
>   * Ability to introduce specialized for type of range methods. For
>     example, |Timespan| could have |Duration toDuration()| method,
>     potential |IntegerRange| could have something like |LongRange
>     toLongRange()| dur to limitations of comparability between
>     classes. This would be impossible with structural class Range
>     without declaring additional static utility methods.
>   * Enhanced validation of annotaion targets as classes, unlike
>     generics, arent erased.
>
>
>       Disadvantages
>
>   * Increased amount of classes to maintain.
>   * Additional considerations would be required before extending Range
>     interface in case if hierarchy non-sealed to ensure backward
>     compatibility.
>
>
>   API Description
>
>
>       NB: Since date ranges is supposed to be one of the most popular
>       if not the most popular use case for range, date-time libraries
>       were main reference for interface design.
>
> ------------------------------------------------------------------------
>
>
>     Section: Bounds
>
>
>       General notes
>
>   * In *Boost Date_Time* (|time_period.begin()|), the start and end
>     are always defined, meaning there is no concept of unbounded
>     intervals. Similarly, some libraries like *Chrono* in Rust assume
>     bounded intervals by default. In fact, only a few libraries expose
>     trully unbound ranges. Although, while complexity of
>     implementation is increased by this corner cases, thier
>     performance also vastly increased by cutting amount of operations
>     in each method at least in half (For two-way unbound interval,
>     almost all operations return constnat value).
>
> ------------------------------------------------------------------------
>
>
>       |T start()|
>
> *Description:*
> Returns the start of the range. If the range is unbounded at the 
> start, this method throws an |UnsupportedOperationException|. This can 
> be preemptively checked using |isBoundedAtStart()|.
>
> *Alternatives:*
>
>   * Method could return |Optional<T>| instead of throwing an
>     exception. I see this two approaches roughly identical in terms of
>     pros/cons score, so suggestions are much appreciated.
>
> ------------------------------------------------------------------------
>
>
>       |T end()|
>
> *Description*:
> Returns the end of the range. If the range is unbounded at the end, 
> this method throws an |UnsupportedOperationException|. Use 
> |isBoundedAtEnd()| to check if the range is bounded.
>
> *Alternatives:*
>
>   * Simallarly to start(), method could return |Optional<T>| instead
>     of throwing an exception. I see this two approaches roughly
>     identical in terms of pros/cons score, so suggestions are much
>     appreciated.
>
> ------------------------------------------------------------------------
>
>
>       |boolean isBoundedAtStart()|
>
> *Description*:
> Returns |true| if the range is bounded at the start. If unbounded, it 
> returns |false|, meaning calling |start()| will throw an 
> |UnsupportedOperationException|.
>
> *Alternatives*:
>
>   * *Joda-Time*, *NodaTime*, *Luxon*, and *Moment.js* do not
>     explicitly support unbounded intervals by default but can use
>     |null| or special values to represent unbounded starts.
>   * *Boost Date_Time* and *Chrono* don’t support unbounded ranges
>     directly, so this method is unnecessary.
>
> ------------------------------------------------------------------------
>
>
>       |boolean isBoundedAtEnd()|
>
> *Description*:
> Returns |true| if the range is bounded at the end. A false value means 
> the range is unbounded at the end, and calling |end()| will throw an 
> |UnsupportedOperationException|.
>
> *Alternatives*:
>
>   * Similar to |isBoundedAtStart()|, most libraries don’t have
>     built-in unbounded intervals, but the concept can be simulated
>     using |null|, minimal/maximal possible value etc. Pros and cons
>     were described in API notes.
>
> ------------------------------------------------------------------------
>
>
>     Section: boolean operations
>
>
>       |boolean contains(T instant)|
>
> *Description*:
> Returns |true| if the given |instant| falls within the start and end 
> bounds of the range, otherwise returns |false|.
>
> *Similar Methods in other libraries*:
>
>   * *NodaTime (|Interval.Contains|)*
>   * *Joda-Time (|Interval.contains|)*
>   * *Luxon (|Interval.contains|)*
>   * *Boost Date_Time (|time_period.contains()|)*
>   * And many others...
>
> *Differences with existing APIs*:
>
>   * *Moment.js* doesn’t provide a direct |contains| method but the
>     |moment-range| plugin adds this functionality with |range.contains()|.
>
> *Note*: this method is present in most interval implementations. 
> Terefore, I concider as basic and unremovable from the API.
>
> ------------------------------------------------------------------------
>
>
>       |boolean overlaps(Range<? extends T> other)|
>
> *Description*:
> Checks if the current range overlaps with another range. Returns 
> |true| if the two ranges overlap, otherwise returns |false|.
>
> *Similar Methods in other libraries*:
>
>   * *NodaTime (|Interval.Overlaps|)*
>   * *Joda-Time (|Interval.overlaps|)*
>   * *Luxon (|Interval.overlaps|)*
>   * *Boost Date_Time (|time_period.intersects()|)*
>   * And many others...
>
> *Differences with existing APIs*:
>
>   * *Moment.js*: The |moment-range| plugin provides a similar
>     |overlaps()| method to check overlap.
>   * *Chrono* relies on custom interval intersection logic.
>
> *Note*: this method is present in most interval implementations. 
> Terefore, I concider as basic and unremovable from the API.
>
> ------------------------------------------------------------------------
>
>
>       General notes on next two methods
>
> Most of the libraries propose API like |isBefore(T point)| or do not 
> provide methods like this at all. Since current implementation throws 
> an exception if interval is not bounded, trivial check for 
> |isBefore| could become 4-6 lines long. The question basically comes 
> down to whether the Range class should be more data-structure-like or 
> object-like. I would argue that at least |isBefore(T moment)| is 
> required, especially since ranges can be negative currently. Existence 
> of boolean isBefore(Range<? extends T> other)|and similar|isAfter` is 
> up to discussion.
>
>
>       |boolean isBefore(Range<? extends T> other)|
>
> *Description*:
> Returns |true| if the current range is strictly before another range 
> (i.e., ends before the other range starts).
>
> *Differences with other libraries*:
>
>   * *NodaTime*: You’d manually compare |End| of one interval with the
>     |Start| of another.
>   * *Joda-Time*: Manual comparison with |Interval.getEnd()| and
>     |Interval.getStart()|.
>   * *Boost Date_Time* and *Chrono* would use custom logic to compare
>     |time_period| or ranges of time, since they don’t have a direct
>     equivalent of |isBefore()|.
>
> *Alternatives*
>
>   * Most of the libraries propose API like |isBefore(T point)| or do
>     not provide methods like this at all. Since current implementation
>     throws an exception if interval is not bounded, trivial check for
>     |isBefore| could become 4-6 lines long. The question basically
>     comes down to whether the Range class should be more
>     data-structure-like or object-like. I would argue that at least
>     |isBefore(T moment)| is required, especially since ranges can be
>     negative currently
>
> ------------------------------------------------------------------------
>
>
>       |boolean isAfter(Range<? extends T> other)|
>
> *Description*:
> Returns |true| if the current range is strictly after another range 
> (i.e., starts after the other range ends).
>
>   * *Similar Methods*:
>       o Similar to |isBefore()|, manual comparisons are used in
>         *NodaTime*, *Joda-Time*, and *Luxon* an others.
>
> ------------------------------------------------------------------------
>
>
>       |boolean isBefore(T point)|
>
> *Description:*
> Determines if the span ends before the given point. This is useful 
> when you need to check whether a time span occurs entirely before a 
> specific point.
>
> *Alternatives*:
>
>   * Method could be removed from APi at all, if Range is desired to be
>     skewed towards being data structure.
>
> ------------------------------------------------------------------------
>
>
>       |boolean isAfter(T point)|
>
> *Description:*
> Determines if the span starts after the given point. This is useful 
> when you need to check whether a time span occurs entirely after a 
> specific point.
>
> *Alternatives*:
>
>   * Similarly to |boolean isBefore(T point)|, method could be removed
>     from APi at all, if Range is desired to be skewed towards being
>     data structure.
>
> ------------------------------------------------------------------------
>
>
>       |boolean isNegative()|
>
> *Description*:
> Returns |true| if the start of the range is after the end, indicating 
> a "negative" range.
>
> *Alternatives:*
>
>   * if concidered too niche, negatie timespans could be removed from
>     model.
>
> *Note:* this one is most questionable for me. Do we really need 
> negative ranges? This is most entirely required in numeric ranges and 
> diagrams, while introdcues huge complexity overhead for majority that 
> doesnt need this feature. Negativity might be confusing for users. 
> Would love to hear thoughs on this matter
>
> ------------------------------------------------------------------------
>
>
>     Section: Range arithmetics
>
>
>       |Optional<Range<T>> intersection(Range<? extends T> other)|
>
> *Description*:
> Returns the intersection of the current range with another range. If 
> the ranges do not overlap, the result is an empty |Optional|. If they 
> overlap, the intersection is returned.
>
> *Similar Methods*:
>
>   * *NodaTime (|Interval.Intersection()|)*
>   * *Moment.js (via |moment-range|, |range.intersect()|)*
>   * *Joda-Time (|Interval.overlap()|)*
>   * And many others...
>
> *Differences with existing APIs*:
>
>   * *Boost Date_Time* returns an empty |time_period| if no overlap
>     exists, instead of an |Optional|. Some libraries return
>     |null| (e.g., *NodaTime*).
>   * Other libraries return null if intervals arent overlapping. This
>     is undesrable, so optional returned instead.
>
> *Note*: this method is present in most interval implementations. 
> Terefore, I concider as basic and unremovable from the API.
>
> ------------------------------------------------------------------------
>
>
>       |Range<T>[] union(Range<? extends T> other)|
>
> *Description*:
> Returns the union of two ranges. If the ranges overlap, the result is 
> a single combined range. If they do not overlap, the result is an 
> array of two separate ranges.
>
> *Differences with existing APIs*:
>
>   * *NodaTime* and *Joda-Time* support similar logic using custom
>     union handling.
>   * *Boost Date_Time* has no built-in |union()| function but you can
>     write custom logic to combine or separate intervals.
>
> *Note*: Behaviour of this method is up to change. Currently, it 
> returns array for maximal performance, but it can (and most likely 
> should) be wrapped in some monadic class. As an alternative, there may 
> be support for non-continuous ranges (ones with gaps inside them), 
> then this method should return thise kind of range.
>
> ------------------------------------------------------------------------
>
>
>       |Optional<Range<T>> gap(Range<? extends T> other)|
>
> *Description*:
> Returns the gap between two ranges, if they do not overlap. If they 
> overlap, the result is an empty |Optional|.
>
> *Differences with existing APIs*:
>
>   * *NodaTime* and *Joda-Time* support custom logic to calculate the
>     gap using |isBefore()|, |isAfter()|, and manual calculations of
>     the gap.
>   * Other libraries return null if intervals are overlapping. This is
>     undesrable, so optional returned instead.
>
> ------------------------------------------------------------------------
>
>
>     Section: potential methods
>
>
>       |boolean isEmpty()|
>
> *Description:*
> Determines if the range is "empty,"
>
> Empty range is its own, separate type of range (basically opposite of 
> unbounded range). There are many questions regrading this type of 
> range. Is it bounded at start or end? If so, what should |start()| or 
> |end()| return. Them throwing an exception would violate current 
> contract between |IsBoundedAtX()| and 'x()` methods.
>
> *Advantages*
>
>   * Returning empty range instead of Optional might be more user-friendly
>
> *Disadvantages*
>
>   * One more concept in the API model
>   * Corner case in |IsBoundedAtX()| and 'x()` contract.
>
>
>       Potential Methods for API Enhancement
>
> In this section, we explore methods that could be added to the API, 
> comparing them with similar functionality in popular time-related 
> libraries. These methods enhance the versatility and clarity of the 
> |Range<T>| implementation, especially in the context of temporal, 
> numeric, and other domain-specific ranges. Some of these methods are 
> inspired by well-established libraries, while others are novel 
> suggestions.
>
> ------------------------------------------------------------------------
>
>
>       |boolean encloses(Range<? extends T> other)|
>
> *Description*:
> Checks whether the current range completely encloses another range, 
> i.e., the other range starts after or at the start of the current 
> range and ends before or at the end of the current range.
>
>  *
>
>     *Similar Methods in Other Libraries*:
>
>       o *NodaTime (|Interval.ContainedBy|)*
>       o *Joda-Time (|Interval.contains|)*
>       o *Luxon (|Interval.contains|)*
>       o *Boost Date_Time (|time_period.contains()|)*
>  *
>
>     *Differences with Existing APIs*:
>
>       o Some libraries handle |encloses()| and |contains()| in the
>         same method. For clarity, this API can separate the two, where
>         |contains()| is used for checking individual points and
>         |encloses()| is for range-level comparison.
>
> ------------------------------------------------------------------------
>
>
>       |boolean abuts(Range<? extends T> other)|
>
> *Description*:
> Returns |true| if the current range abuts (i.e., touches but does not 
> overlap) with another range. This method is useful when determining 
> whether two ranges are adjacent but do not overlap.
>
>  *
>
>     *Similar Methods in Other Libraries*:
>
>       o *NodaTime (|Interval.Abuts|)*
>  *
>
>     *Alternatives*:
>
>       o Instead of this method, users could manually compare the
>         |end| of one range and the |start| of another, but including
>         |abuts()| in the API simplifies the logic and reduces
>         error-prone comparisons.
>
> ------------------------------------------------------------------------
>
>
>       |Range<T> extendTo(T point)|
>
> *Description*:
> Returns a new range that extends the current range to include the 
> given point. If the point is already within the range, it returns the 
> current range. Otherwise, it extends either the start or end, 
> depending on the point's position relative to the range.
>
>  *
>
>     *Similar Methods in Other Libraries*:
>
>       o *NodaTime* and *Joda-Time* do not have explicit methods for
>         this, but users can manipulate intervals manually.
>       o *Moment.js*: The |moment-range| plugin offers similar logic
>         via manual adjustments to the range.
>  *
>
>     *Advantages*:
>
>       o In contrast to manual adjustment, this method automates the
>         process of extending ranges, which can be useful in situations
>         where ranges need to be dynamically modified over time (e.g.,
>         expanding time intervals in streaming data).
>
> *Alternatives*:
>
>   * Users could manually adjust the range using |start()| and
>     |end()| "withers", but an explicit |extendTo()| method offers a
>     more intuitive, built-in approach
>
> ------------------------------------------------------------------------
>
>
>       |Range<T> shrinkTo(T point)|
>
> *Description*:
> Returns a new range that shrinks the current range to exclude the 
> given point, if possible. If the point is within the range, the range 
> is modified so that it no longer includes the point. This is useful 
> for splitting ranges or excluding unwanted time periods or values.
>
>   * *Similar Methods in Other Libraries*:
>     No major time libraries provide a direct equivalent to this
>     functionality, although similar operations can be manually
>     performed by manipulating |start| and |end|.
>
> *Alternatives*:
>
>   * Similarly to |extendTo|, users could manually adjust the range
>     using |start()| and |end()| "withers", but an explicit
>     |shrinkTo()| method offers a more intuitive, built-in approach.
>
> ------------------------------------------------------------------------
>
>
>       |Range<T>[] difference(Range<? extends T> other)|
>
> *Description*:
> Returns the difference between the current range and another range 
> (XOR operations). If the ranges overlap, the result is a new range or 
> two ranges representing the non-overlapping portions. If the ranges do 
> not overlap, the result is the current range.
>
> *Adavntages*:
>
>   * This method simplifies computing the difference between two
>     ranges, reducing the need for manual boundary comparisons.
>   * Completes set of methods required for ranges arithmetics
>
> *Disdavntages*:
>
>   * THis method is inverse of |union(Range<? extends T> other)|, so it
>     has same design problems as union.
>
> ------------------------------------------------------------------------
>
>
>       |Range<T> clamp(Range<? extends T> bounds)|
>
> *Description*:
> Clamps the current range to fit within the specified bounds. If the 
> current range extends outside of the bounds, it is shortened to fit 
> within the bounds. If the range already fits within the bounds, it is 
> returned unchanged.
>
> *Advantages*:
>
>   * This method streamlines the process of adjusting a range to a set
>     of bounds, which is especially useful in time-based operations
>     where ranges must be constrained within specific periods (e.g.,
>     scheduling).
>
> ------------------------------------------------------------------------
>
>
>       |boolean isContiguousWith(Range<? extends T> other)|
>
> *Description*:
> Determines if the current range is contiguous with another range, 
> meaning that the two ranges touch or overlap without leaving any gaps. 
> This is particularly useful when combining ranges or ensuring that a 
> sequence of ranges forms a continuous block.
>
> *Alternatives*:
>
>   * Users could manually compare the |end| and |start| of ranges to
>     check contiguity, but this method offers a more explicit and
>     efficient way to perform the check.
>
> ------------------------------------------------------------------------
>
>
>       |Optional<Range<T>> asBounded()|
>
> *Description*:
> Returns the bounded version of the current range, if one exists. If 
> the range is already bounded, it returns the range unchanged. If the 
> range is unbounded, the result is an empty |Optional|. Could be used 
> as a monade for handling errors if range that is expected to be 
> bounded, but unbounded one has been recieved.
>
> *Alternatives:*
>
>   * API could explicitly expose BoundedRange marker (or not marker)
>     interface to verify range that is recieved is bounded at compile
>     time. Interface could provide some adapter methods for converting
>     unknown-boundness ranges to bounded, and have specific behaviour
>     for error cases.
>
>
>       |Range<T>[] splitAt(T point)|
>
> *Description*: Splits the current range into two sub-ranges at the 
> specified point. If the point lies outside the range, it returns an 
> array of length 1 with initial range. If rang |contains()| point, than 
> array of length 2 is returned, whith two ranges splitted accross given 
> point.
>
>
>       |List<Range<T>> splitInto(int n)|
>
> *Description*:
> Splits the current range into |n| equal sub-ranges. If the range 
> cannot be evenly divided, the last range may be slightly larger to 
> accommodate the remaining span. Throws 
> |UnsupportedOperationException| if range is at least half-unbounded.
>
>
>       |Stream<T> pointsFromStartToEnd(??? step)|
>
> *Description*: Returns a list of points that are evenly spaced from 
> the start to the end of the range, using the specified step size. 
> Throws |UnsupportedOperationException| if range is 
> |isBoundedAtStart()| returns false.
>
> *Note:* while this method could have various use cases, It is not 
> clear how step could be provided. One of the options is to pass 
> |Function<T, T>| that is invoked on each value until value is |> 
> end(|) instrad of constant step.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/core-libs-dev/attachments/20240923/d4745150/attachment-0001.htm>


More information about the core-libs-dev mailing list