Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

What's the point of this 'overload' idiom?

by Cody Fendant (Hermit)
on Dec 07, 2022 at 04:23 UTC ( [id://11148637]=perlquestion: print w/replies, xml ) Need Help??

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

I'm working with some code which frequently uses this … idiom I guess you'd call it?

use overload '""' => sub {shift->name;};

So I've read the Perldoc and it has this example:

package Number; use overload [snipped] '""' => sub { ...; };

which says it's "an anonymous subroutine to implement stringification: this is called whenever an object blessed into the package Number is used in a string context (this subroutine might, for example, return the number as a Roman numeral)."

So I guess, something like this?

### example package package Number; my $self = {}; sub new { my $class = shift(); $self->{decimal} = shift(); return bless( $self, $class ); } my @roman_numbers = qw[ undef I II III IV V VI VII VIII IX X ]; use overload '""' => sub { return $roman_numbers[ $self->{decimal} ] }; 1; ### main package main; my $instance = new Number(3); print $instance; ### it will print 'III'

So, two questions:

  1. Why would I want this particularly?
  2. what's the specific thing in use in the code I'm seeing, use overload '""' => sub {shift->name;}; and why would I want that?

Replies are listed 'Best First'.
Re: What's the point of this 'overload' idiom?
by hv (Prior) on Dec 07, 2022 at 04:56 UTC

    Perl is all about "if you treat it as a string, it'll try to behave as a string". If you treat a number as a string, it'll give you the number in decimal form; if you treat an object as a string, it will give you the class, reftype and refaddr (like Object=HASH(0x559ee79cc1e0)).

    Some objects are intended to behave like other things, for example Math::BigInt objects are intended to behave like numbers, so when they are stringified you don't want them to show Math::BigInt=HASH(0x559ee79cc1e0), you want them to stringify as a number the same as a normal integer would. That's what this use of overload is doing.

    Overloading means you can do this:

    % perl -MMath::BigInt -E 'my $x = Math::BigInt->new(2)**100; say $x' 1267650600228229401496703205376 %

    There is overloading of the ** operator going on here, and then stringification overload for the say.

    Overloading as a general concept is something you want very rarely, but is very useful when you do want it. Once you are aware of it, the main skill is knowing to use it very sparingly - it is usually a really bad idea to have your work code overload the Bank::Account class to make $account += $amount credit the account. But I happen to write a lot of maths code with big integers, and for that context it is incredibly useful.

      Overloading as a general concept is something you want very rarely, but is very useful when you do want it.

      I really like that statement. Well put.

      One area where overloading is highly desired is during unit testing. Although I'd classify it as 'overwriting' rather than overloading, the premise is the same. Make a function do something different than what the original does. For example, Mock::Sub does exactly this. Overriding functions during testing happens very often and is hugely useful.

        I might be wrong, but I think the technical terms for overwriting functions are I'd use overloading explicitly for perlops

        But this might be restricted to Perl lingo and different in other languages (the line between operators, built-ins and functions is blurred and depends on context)

        And I have to admit that I'm anal about terminology, a "maladie professionnelle" of studying math...

        Cheers Rolf
        (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
        Wikisyntax for the Monastery

      Thanks for that. If you don't mind, what's the point of the specific thing where it's not a code reference in there but shift->name;?

        shift->name

        Is a lazy man's way of saying:

        sub thing { my ($self) = @_; return $self->name; }

        In other words, it's a quick way of not declaring the 'self' variable. I do it a different way if I have a one line sub:

        sub name { return $_[0]->{name}; }

        ...the following is equivalent:

        sub name { return shift->{name}; }

        When a Perl method receives its parameters, the first one is always the calling object. shift or $_[0] is the same thing; the object itself. If a method is more than one line, I prefer to collect the parameter as self (eg. my ($self) = @_;). That's not always the case.

        Note that shift->thing; only works once... once you've shifted the object off the stack, you can't call shift again. In these cases, $_[0] is better, but if you have to use $_[0] more than once, you're far better off for self-documenting purposes to use my ($self) = @_;.

Re: What's the point of this 'overload' idiom? (updated)
by LanX (Saint) on Dec 07, 2022 at 14:08 UTC
    > 1. Why would I want this particularly?

    Operator overloading is a way to adapt operators (not functions) to an object class.

    For example to define a different algebra° like complex numbers

    Most operators in Perl are designed for number and string types.

    Example: If you had $x and $i of a class "ComplexNumber" and wrote $i**$x without overloading, you'd only get the the result from the implicitly numified references. That's hardly useful ...

    (Update: a more complex example for DateTime objects further down)

    It can also help adding syntactic sugar to self defined internal DSLs.

    But it's a complicated matter because you have to keep many side effects in mind when designing such an interface. Like precedence.

    > 2. what's the specific thing in use in the code I'm seeing, use overload "" => sub {shift->name;}; and why would I want that?

    Because otherwise the stringification of an object will be the string form of the ref-adress, which isn't helpful.

    DEMO > perl -de0 DB<2> use IO::Handle; # just an arbitrary class from co +re DB<3> $io = IO::Handle->new(); DB<4> p "$io" # <-- Stringification IO::Handle=GLOB(0x32c5c70) DB<5> p 0+$io # <-- Numification 53238896 DB<6> p $io + $io # nummeric operation 106477792 DB<7> p $io . $io # string operation IO::Handle=GLOB(0x32c5c70)IO::Handle=GLOB(0x32c5c70) DB<8>

    But overloading "" will make an object return something more meaningful instead in string context.

    Like it's name from $self->name (sic)

    HTH!˛ :)

    Cheers Rolf
    (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
    Wikisyntax for the Monastery

    °) (abstract) algebra means a set of things and allowed operations mapping set members to other members of the same set.

    for instance integer arithmetic is an algebra different to real number arithmetics.

    But division is tricky here, the result of 3/2 (integers) is 1.5 (real number) in Perl. Compare Python where 3/2 is 1, to keep the algebra clean.

    This different interpretations could be fixed by overloading, tho I doubt that's easily done for native types in Perl.

    Moreover, in Perl operators rule determine types, while in Python it's the other way round. Best (worst ;) example of mixing those two approaches is JS where 1+1 can occasionally be 11

    a='1'; 1+a '11'

    simply put: Overloading is a way to let types (classes) determine operators.

    ˛) That's a complicated but interesting subject. I tried my best to come up with good examples, please keep asking if you need more elaboration.

    further update

    have a look at the even more complicated overloading in DateTime#Overloading

    my $new_dt = $dt + $duration_obj;

    two classes are interacting here DateTime and DateTime::Duration

    without overloading you'd need to use

    $dt->add_duration($duration_object)

    as you can see are overloaded operators a mean to DWIM.

Re: What's the point of this 'overload' idiom?
by ikegami (Patriarch) on Dec 08, 2022 at 21:25 UTC

    Because

    say $instance;
    is nicer than
    say $instance->as_string;

    That's it. Because it's nice.

    And it's not like it takes anything away. When's the last time you saw Class=HASH(0xDEADBEEF) and thought it was useful?[1]

    But it's best to give the choice, though.

    use overload '""' => \&as_string; sub as_string { my $self = shift; ... }

    1. The overload module provides a function to get that stringification if you really do need it. Also, the referenced var type, its address and the class associated with it are all available through Scalar::Util functions.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11148637]
Front-paged by stevieb
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (2)
As of 2024-04-20 05:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found