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