Hi Paul,
@mod -> remainder: I am glad you went with the clean solution of a
breaking change; sometimes alas that is the only good option, and, as
you said, every other (clever) approach just feels like putting a
band-aid on a band-aid, ultimately ending in a mess.
@OperationRename: That sounds like something that fits well with the
large number of existing Groovy annotations that are useful for a wide
array of scenarios.
Of course everybody that uses this will be responsible for not abusing
it, but in a sense, as software developers, we are all a bit
Spiderman... what do you say, ChatGPT ?
ChatGPT: "People hold a variety of opinions on this topic. For instance
... <goes on for another 3 pages>"
Cheers,
mg
On 23/03/2023 02:43, Paul King wrote:
Hi folks,
It has been a while but I finally got back around to this issue.
As a reminder, this issue is about using "mod" as the name of the
method for the "%" operator. Both remainder and mod are the same for
positive numbers, and we are guilty in informal contexts of sometimes
conflating the names, but they differ for negative numbers. This
caused a difference (only for negative numbers) for BigIntegers. In
the earlier email, I was going to look at "patches" which would allow
us to keep "mod" as the operator name. I tried numerous "fixes" but
they all seemed like patches on top of patches rather than a clean
solution. So, instead I went with the solution (which I previously
described as "somewhat intrusive") of renaming the name of the
operator method to "remainder". This makes it a breaking change for
Groovy 5 (for -ve numbers and also anyone using the "mod" method name
relying on DSL-like code) but arrives at a much cleaner solution.
I have created the following PR here:
https://github.com/apache/groovy/pull/1878
To minimise the impact on existing users, I added a new AST
transform, @OperationRename, which could be used by anyone affected by
the change when writing DSL-like code using "mod". This also has the
advantage of giving another option when wanting to use operator
overloading with existing libraries that might not use the method
names Groovy uses, e.g. subtract/add/times instead of
minus/plus/multiply. We could also look at some metaclass tweaking so
that the runtime looks for "mod" as a fallback for "remainder" before
jumping to method missing but I'd probably do that as a second pass
only if there is sufficient interest.
Thoughts?
Cheers, Paul.
On Fri, Oct 28, 2022 at 9:34 PM Paul King <pa...@asert.com.au> wrote:
Hi folks,
As part of fixing GROOVY-10800, I was planning to make the behavior
for the "%" operator for BigInteger be consistent with our other data
types (and with Java).
Basically, there is a distinction between "remainder" and "modulo" for
negative numbers.
For the expression "numerator op divisor", they will be the same for
positive numbers but for negative numbers, "remainder" will return a
negative number for a negative numerator while "modulo" will always
return a number "0 <= result < divisor". You can get one from the
other by adding the divisor to a negative result from "remainder".
What is sometimes a little confusing is that the "remainder" operator
(%) is often informally referred to as the "mod" operator (since they
are the same for positives). Indeed, we use "mod" as the name of the
method to use for operator overloading purposes.
Currently the behavior is:
def nums = [-10, -10L, -10f, -10d, -10G, -10.0G]
assert nums.collect{ it % 3 } == [-1, -1, -1f, -1d, 2G, -1.0G]
(Note: The BigDecimal result relies on GROOVY-10786, so currently only
in master.)
Changing the behavior is easy (albeit breaking for negatives) but
there is a knock on consequence. Since we use "mod" as our method for
operator overloading, the BigInteger "mod" method is then no longer
available.
For Groovy 5, we could go and rename our operator overloading method
from "mod" to "remainder" or some such but it is quite an intrusive
change.
There is a workaround which we could document:
def negTen = -10G
assert 2G == negTen.modPow(1, 3)
And/or we could provide a "modulo" extension method on BigInteger
to allow:
assert 2G == negTen.modulo(3)
This last approach was what I was thinking of doing.
Thoughts?
Cheers, Paul.