syphilis has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

I have a module A that overloads the '-' operator via its A::oload_minus() subroutine.
And I have a second module B that also overloads the '-' operator via it's own B::oload_minus() subroutine.
Both modules also have their own oload_add, oload_mul, oload_div, oload_mod and oload_pow subroutines that overload the other basic arithmetic operators).

I have constructed the overloading in module A to handle B objects.
But if module B's overloading subroutines are passed a module A object, it is (by my current design) a fatal error.
use A; use B; $A_obj = A->new(16); $B_obj = B->new(6); my $n1 = $A_obj - $B_obj; # $n1 is an A object with value 10 my $n2 = $B_obj - $A_obj; # Fatal error
In the above demo I want $n2 to be an A object, with the value of -10.
That is, I want the A::oload_minus() sub to receive the args ($A_obj, $B_obj, TRUE).
Instead, the B::oload_minus() sub is receiving the args ($B_obj, $A_obj, FALSE) - which is, by my current design, a fatal error since B::overload_minus() does not currently accept A objects.

Is there a way that I can work around this without making any changes to the B module ? (The motivation to not alter module B is simply that I don't want to add more clutter to B unless I need to.)

My module "A" is in fact Math::MPC, and my module "B" is in fact Math::MPFR.

AFTERTHOUGHT: I should point out that the arithmetic overloading in the publicly available versions of Math::MPC don't yet accept Math::MPFR objects. (I've currently implemented this new feature on my local Math::MPC builds only.)

Cheers,
Rob

Replies are listed 'Best First'.
Re: A little overloading conundrum
by swl (Prior) on Mar 07, 2026 at 03:42 UTC

    There's nothing I can think of and the documentation suggests it's not something that is easy to do.

    One could reverse the order so $A_obj is checked first: my $n2 = -$A_obj + $B_obj;. That's obviously not practical, though, and would also need neg overloading on $A_obj (and addition if not already done).

      There's nothing I can think of and the documentation suggests it's not something that is easy to do.

      Yes - the first point I read in that link to the documentation pretty much kills all hope:
      1.If the first operand has declared a subroutine to overload the opera +tor then use that implementation.
      I suppose (untested) I could do:
      my $n2 = (\$B_obj) - $A_obj;
      and that would at least call module A's oload_minus() subroutine .... which would then be structured to de-reference the first argument and return the intended result.
      But that solution is no more practical than the alternative you provided.

      Thanks for the reply. I had, of course, consulted the overloading docs but had stopped reading before reaching the bit to which you linked.
      It's not the end of the world if I have to modify module B.

      Cheers,
      Rob
        If you are working around it in the caller, this is clearer, if not as efficient:
        my $n2 = -($A_obj - $B_obj);
Re: A little overloading conundrum
by choroba (Cardinal) on Mar 08, 2026 at 18:29 UTC
    You can do ugly things to B even from within A:
    #!/usr/bin/perl use warnings; use strict; use experimental qw( signatures ); { package My::A; use overload '-' => \− sub minus($x, $y, $swap) { return ref($x)->new( ($y->isa('My::B') ? $x->[0] - $y->{value} : ($x->[0] - $y->[0]) ) * (1, -1)[$swap] ) } sub new($class, $value) { bless [$value], $class } # We can change My::B outside of B, in fact. eval q{ package My::B; my $m = My::B->can('minus') or die 'My::B not loaded'; use overload "-" => sub ($x, $y, $s) { return $y->isa('My::A') ? $y->minus($x, 1) : $m->($x, +$y, $s) } }; } { package My::B; # Can't be changed! use overload '-' => \− sub minus($x, $y, $swap) { my $subtr = $x->{value} - $y->{value}; return ref($x)->new($swap ? -$subtr : $subtr) } sub new($class, $value) { bless {value => $value}, $class } } my $a0 = 'My::A'->new(16); my $a1 = 'My::A'->new(6); my $b0 = 'My::B'->new(16); my $b1 = 'My::B'->new(6); use Data::Dumper; print Dumper A => $a0 - $a1; print Dumper B => $a0 - $b1; print Dumper C => $b0 - $b1; print Dumper D => $b0 - $a1;
    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      You can do ugly things to B even from within A

      Yes - and my overloading of the arithmetic operations are done in XSubs - which leads to a fair bit of code clutter, but is procedurally clean and simple (IMO, at least).
      I actually don't fully understand the code you've provided, but I'll certainly try to remedy that when I get the chance. (I'm not really a proper perl programmer.)

      Having decided that Math::MPC's basic arithmetic operators should be capable of handling Math::MPFR objects (since the MPC library already provides functions that perform such arithmetic), it's a fairly simple exercise to insert the few required lines of code into the relevant Math::MPC XSubs.
      If not for this "conundrum", that would be the end of the exercise.
      Math::MPFR, which has no business to engage in arithmetic with Math::MPC objects, could be left as is - with no need to even mention this new capability in the Math::MPFR docs.

      The solution I use is simple enough, but it involves making changes to the Math::MPFR overloading XSubs.

      It's only a matter of inserting one line of code into a new if(condition){} block in each of those XSubs. And it just does a callback to (eg) Math::MPC::overload_minus(mpc_object, mpfr_object, &PL_sv_yes) with the assistance of a 20-line macro.
      I'm happy enough with that, and I've used that technique without issue in a number of cross-class overloading situations - though I have no idea how expensive that solution is.
      There's even no need to check whether Math::MPC has been loaded. If a Math::MPC object has been passed to a Math::MPFR function then we know that perl has loaded Math::MPC.
      It's just a bit annoying that I have to go that extra step.
      It would be much better if I could simply tell perl's overloading process "hey, in this case, use the overloading subroutine of the second object". But ... I can't do that :-((

      Cheers,
      Rob
        I actually don't fully understand the code you've provided, but I'll certainly try to remedy that when I get the chance.

        First up, I wanted to remove the "experimental" requirement - assuming it's not a crucial piece of the demo. (I'm not at all familiar with experimental stuff, and I don't have much interest in it.)
        I've also added some dumps of some more subtractions.
        So, I've arrived at this modified version of choroba's script :
        use warnings; use strict; #use experimental qw( signatures ); { package My::A; use overload '-' => \− # sub minus($x, $y, $swap) { ############ Replaced by next line ## sub minus { my($x, $y, $swap) = (shift, shift, shift); return ref($x)->new( ($y->isa('My::B') ? $x->[0] - $y->{value} : ($x->[0] - $y->[0]) ) * (1, -1)[$swap] ) } # sub new($class, $value) { ############ Replaced by next line ## sub new { my($class, $value) = (shift, shift); bless [$value], $class } # We can change My::B outside of B, in fact. eval q{ package My::B; my $m = My::B->can('minus') or die 'My::B not loaded'; use overload "-" => sub { my($x, $y, $s) = (shift, shift, + shift); return $y->isa('My::A') ? $y->minus($x, 1) : $m->($x, +$y, $s) } }; } { package My::B; # Can't be changed! use overload '-' => \− # sub minus($x, $y, $swap) { ############ Replaced by next line ## sub minus { my($x, $y, $swap) = (shift, shift, shift); my $subtr = $x->{value} - $y->{value}; return ref($x)->new($swap ? -$subtr : $subtr) } # sub new($class, $value) { ############ Replaced by next line ## sub new { my($class, $value) = (shift, shift); bless {value => $value}, $class } } my $a0 = 'My::A'->new(16); my $a1 = 'My::A'->new(6); my $b0 = 'My::B'->new(16); my $b1 = 'My::B'->new(6); use Data::Dumper; print Dumper A => $a0 - $a1; print Dumper B => $a0 - $b1; print Dumper C => $b0 - $b1; print Dumper D => $b0 - $a1; print Dumper AA => $a1 - $a0; print Dumper BB => $b1 - $a0; print Dumper CC => $b1 - $b0; print Dumper DD => $a1 - $b0;
        At this point, I have 2 questions:
        1. Where is can documented ?
        2. How is it that I can use isa even though no features have been enabled ?

        Regarding the first question:
        >perldoc -f can No documentation for perl function 'can' found
        WRT the second question, the perlop documentation says this regarding isa:
        This feature is available from Perl 5.31.6 onwards when enabled by + "use feature 'isa'". This feature is enabled automatically by a "use v5 +.36" (or higher) declaration in the current scope.
        I have done neither of those things. Has perl-5.42.0 automatically called that feature for me ? ... or does it automatically call "use v5.36" ?

        Cheers,
        Rob