in reply to Re: A little overloading conundrum
in thread A little overloading conundrum

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

Replies are listed 'Best First'.
Re^3: A little overloading conundrum
by syphilis (Archbishop) on Mar 10, 2026 at 06:17 UTC
    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
      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 ?

      From UNIVERSAL:

      UNIVERSAL is the base class from which all blessed references inherit....
      UNIVERSAL provides the following methods:
      $obj->isa( TYPE )
      ...
      $obj->can( METHOD )

      Regarding use feature 'isa', it appears this refers to the experimental isa operator, as opposed to the UNIVERSAL method.

      Hope that helps,

      Athanasius <°(((><contra mundum סתם עוד האקר של פרל,

        Thanks for clarifying that Athanasius.

        I've added some debug print() commands to help me understand the flow, and I've altered the code that performs (and displays the results of) the subtractions.
        Here 'tis:
        use warnings; use strict; { package My::A; use overload '-' => \&minus; sub minus { my($x, $y, $swap) = (shift, shift, shift); print "In My::A minus\n"; return ref($x)->new( ($y->isa('My::B') ? $x->[0] - $y->{value} : ($x->[0] - $y->[0]) ) * (1, -1)[$swap] ) } sub new { my($class, $value) = (shift, shift); bless [$value], $class } # We can change My::B outside of My::B, in fact. eval q| package My::B; print "Running the eval in My::A\n"; my $m = My::B->can('minus') or die 'My::B not loaded';# $m + is a reference to My::B::minus(). use overload "-" => sub { my($x, $y, $s) = (shift, shift, + shift); print "From overload sub in My::A eval\n"; return $y->isa('My::A') ? $y->minus($x, 1) : $m->($x, $ +y, $s) } |; } { package My::B; # Can't be changed! use overload '-' => \&minus; sub minus { my($x, $y, $swap) = (shift, shift, shift); print "In My::B::minus\n"; my $subtr = $x->{value} - $y->{value}; return ref($x)->new($swap ? -$subtr : $subtr) } 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); print "All values assigned\n"; my $r1 = $a0 - $a1; print ref($r1), ' = ', ref($a0), ' - ', ref($a1), "\n\n"; my $r2 = $a0 - $b1; print ref($r2), ' = ', ref($a0), ' - ', ref($b1), "\n\n"; my $r3 = $b1 - $a0; print ref($r3), ' = ', ref($b1), ' - ', ref($a0), "\n\n"; my $r4 = $b0 - $b1; print ref($r4), ' = ', ref($b0), ' - ', ref($b1), "\n\n"; __END__ Outputs: Running the eval in My::A All values assigned In My::A minus My::A = My::A - My::A In My::A minus My::A = My::A - My::B From overload sub in My::A eval In My::A minus My::A = My::B - My::A From overload sub in My::A eval In My::B::minus My::B = My::B - My::B
        I gather that:
        1) the eval in My::A gets executed at start-up and sets its own subtraction subroutine as the subtraction subroutine that My::B's overloading of '-' will call (instead of calling My::B::minus);
        2) it's the same behaviour, irrespective of the order in which packages My::A and My::B are loaded;
        3) it would be the same behaviour if the 2 packages were in separate files (My/A.pm and My/B.pm), irrespective of the order in which the 2 pm files were loaded.

        I certainly didn't know that this was do-able - and thanks, choroba, for drawing my attention to it.
        It's something I can probably utilize wrt the overloading of objects from modules that I don't maintain.

        Are there any caveats regarding the process by which this works ?

        Cheers,
        Rob