On 10.01.24 05:53, Paul King wrote:
Hi folks,

The NumberRange abstraction tries very hard to allow any Number
numeric type to be used but since the Number interface doesn't convey
much behavior, there are a few places where it defaults to using
Groovy's NumberMath plumbing which, to cut a long story short, falls
back to using BigDecimal for any numeric calculations which aren't
using the common known simpler types.

But wouldn't that mean we are lacking a range type for this?

A consequence of this is that currently if you created a range using
e.g. the Apache Commons Fraction class (which does extend Number), and
used a Fraction stepSize, the values in the range would be one
Fraction (for the first element) and then subsequent elements would be
BigDecimals.

@Grab('org.apache.commons:commons-lang3:3.14.0')
import org.apache.commons.lang3.math.Fraction
def r = (Fraction.ONE..2).by(Fraction.ONE_QUARTER)
println r.toList() // => [1/1, 1.25, 1.50, 1.75, 2.00]

This isn't incorrect in one sense but is somewhat surprising. Given
that the Number interface doesn't have operators, providing a smarter
detection of the number system to use becomes somewhat tricky. One
thing we could do is provide some interface that providers could use
and we could have a "Fraction" math implementation that satisfied that
interface.

But we have this convention already, which is substract, add and negate
as dynamic methods being called. It is not a formal interface, yes, but
so is also not for previous and next for the ObjectRange.

Alternatively, we could supply some [Bi]Functions that
offered the supplied behavior that the StepIterator needs when
calculating subsequent elements in the range. With this second
approach, we could do something like:

@Grab('org.apache.commons:commons-lang3:3.14.0')
import org.apache.commons.lang3.math.Fraction
(Fraction.ONE..2).by(Fraction.ONE_QUARTER,
     Fraction::add, Fraction::subtract, Fraction::negate).toList()

Which gives a list of all Fraction instances: [1/1, 5/4, 3/2, 7/4, 2/1]

Is this something we should support? Does anyone have ideas on the
best implementation?

the key point about the Number interface is actually converting to one
of the Java numbers. Of course I understand what you want to achieve,
but is that really a NumberRange in the end?

If I extend and abstract the idea above, don't I end up with something
where I define a step-function:

(Fraction.ONE..2).by(it -> it.add(Fraction.ONE_QUARTER)

and would that not kind of abstract ObjectRange as well:

('a'...'z').by(Character::next)

I like here especially the simplicity, that you really only require
Comparable for this to work.

Then again what does Number.next() do? It is calling plus(1). If we go
to the essence of it we actually have only one real range, which is the
ObjectRange and all other ranges are optimizations.

In fact if you look at it strictly we are even breaking the contract by
not doing next in there, but plus(1). If you would replace next() on
Integer our IntRange will ignore that.

Anyway, since they are optimization for specific cases maybe NumberRange
should not try to solve this, but a new range?


bye Jochen

Reply via email to