On Wed, Jan 10, 2024 at 8:41 PM Jochen Theodorou <blackd...@gmx.org> wrote:
>
> 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?

We could have yet another range type but NumberRange is currently
coded to work for any Number subclass except in just a few places. And
Fraction, being a Number subclass is what I was interested in here.

> > 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.

We have multiply(-1), plus() and minus() but we don't call through to
those methods directly but rather go through the NumberMath plumbing
which ends up calling those methods for the Java Number classes but
defaults back to BigDecimal for any unknown Number subclasses.

> > 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)

Perhaps a step function is the way to go. The current implementation
has some assumptions about counting up or down based on the sign of
the step - that functionality might have to be abandoned if a step
function was used instead of a step size.

> 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?

We could define a new range type. What I am after is something which
does everything the current NumberRange does but doesn't hard-code
BigDecimal arithmetic for non-Java Number subclasses. I'll think a bit
more about it.

> bye Jochen

Reply via email to