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

Hi

I'm trying to work out how/why one might overload '0+' ... and I'm not having much success.
use warnings; use strict; package Soldier; use overload '0+' => \&truncate; my $soldier1 = {NAME => 'BENJAMIN', RANK => 'PRIVATE' , SERIAL => 151.11}; bless $soldier1; my $int = int($soldier1); print $int, "\n"; sub truncate { print "sub truncate called: ", int($_[0]->{SERIAL}), "\n"; return int($_[0]->{SERIAL}); } __END__ OUTPUTS: sub truncate called: 151 sub truncate called: 151 151
Questions:
1) Why does the sub truncate get called twice ?
2) Why would one want to overload the int() function with use overload '0+' => \&truncate; when use overload 'int' => \&truncate; works just as well (and gets called only once when substituted into the above demo script) ?
3) I notice that $soldier1 ? 1 : 0; also calls truncate (just once). I don't know why that is. Under what other circumstances will sub truncate be called ?

Perhaps I just need to see a proper example of the usage of overloading '0+'.

Cheers,
Rob

Replies are listed 'Best First'.
Re: overloading '0+'
by CountZero (Bishop) on Sep 02, 2007 at 22:10 UTC
    The documentation of the module overload says:
    # Transcendental functions

    "atan2", "cos", "sin", "exp", "abs", "log", "sqrt", "int"

    If abs is unavailable, it can be autogenerated using methods for "<" or "<=>" combined with either unary minus or subtraction.

    Note that traditionally the Perl function int rounds to 0, thus for floating-point-like types one should follow the same semantic. If int is unavailable, it can be autogenerated using the overloading of 0+.

    # Boolean, string and numeric conversion

    'bool', '""', '0+',

    If one or two of these operations are not overloaded, the remaining ones can be used instead. bool is used in the flow control operators (like while) and for the ternary ?: operation. These functions can return any arbitrary Perl value. If the corresponding operation for this value is overloaded too, that operation will be called again with this value.

    As a special case if the overload returns the object itself then it will be used directly. An overloaded conversion returning the object is probably a bug, because you're likely to get something that looks like YourPackage=HASH(0x8172b34).

    Overloading '0+' is really overloading numification. So everytime you use $soldier1 in a context where it is used as a number, you call truncate and since as per the docs, int is autogenerated out of '0+' (unless you provide for its own routine), you get to call truncate twice: once to numify $soldier1 and once to take the integer value.

    You therefore really should provide a separate definition for the numification (returning the SERIAL number) and then "overload" int

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      So if we add function to handle the conversion to an integer there should be one call to truncate and other call to the overload integer function.
      #!/usr/bin/env perl # use strict; use warnings; package Soldier; use overload '0+' => \&truncate, 'int' => \&my_int; my $soldier1 = { NAME => 'BENJAMIN', RANK => 'PRIVATE', SERIAL => 151.11 }; bless $soldier1; print int($soldier1), "\n"; sub truncate { print "sub truncate called: ", $_[0]->{SERIAL}, "\n"; return $_[0]->{SERIAL}; } sub my_int { print "sub my_int called: ", $_[0]->{SERIAL}, "\n"; return int($_[0]->{SERIAL}); }
      Produces the (unexpected) output;
      sub my_int called: 151.11 151
      Thoughts?
        Your my_int-routine does not require its argument to be numified as it works directly on the object.

        If you force the object to be numified by changing the argument of int to: $soldier1 + 0 (you will have to overload '+' or add fallback => 1 to the overload hash) you will see truncate being called.

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      Thanks for all of the replies, guys.

      I had read the documentation (numerous times) but found it difficult to understand.

      Overloading '0+' is really overloading numification. So everytime you use $soldier1 in a context where it is used as a number, you call truncate ...

      That's the bit I was failing to understand. Things now start to fall into place.

      If I understand correctly, there's really no advantage in overloading '0+' unless one has a single subroutine that caters for a variety of overloadable numeric operations ?

      Cheers,
      Rob
Re: overloading '0+'
by blokhead (Monsignor) on Sep 02, 2007 at 21:23 UTC
    I don't know the answer to your first question.. But in general, the answer to your other questions is that when perl evaluates an overloaded object in some context (numeric, boolean, string, etc), or as an argument to certain core functions (abs, int, log, sqrt, etc), and there is no specific method for that context, it will "fall back" to other methods in order to find a reasonable value (see perldoc overload).

    In the first example, there is no overload method defined for the int function. However, you have defined one for numeric context. So perl is smart enough to use that and pass the resulting number to the builtin int.

    In the second example, you evaluate the object in boolean context, but no boolean overload method is defined. Perl has a standard way to evaluate numbers in boolean context, so it calls your numeric context method, and interprets the result as a boolean in the normal perl way.

    Most of the time you want an overloaded object to be "consistent" with perl scalars in how they are evaluated in different contexts. So overload.pm is nice and lets you define the minimal number of overloaded methods, and the rest is handled automatically, following perl's rules for regular scalars.

    blokhead

Re: overloading '0+'
by bruceb3 (Pilgrim) on Sep 02, 2007 at 21:24 UTC
    0+ is used whenever the variable is used as a number. 0+ is the conversion to a number not just to an integer. So in the line;
    my $int = int($soldier1);
    The call to int is one call to truncate and then the assignment is the other.

    If you don't have bool overloaded and 0+ is overloaded then, 0+ will be used in a boolean context, which is why truncate is being called in this code;

    $soldier ? 1: 0;

    See overload for the all the gory details. (perldoc overload) The sections titled "Boolean, string and numeric conversion" and "Transcendental functions" answer your questions.

      and then the assignment is the other.

      No. Assignments don't trigger 0+, and the result of the call to int is not an object, so overload doesn't apply to the assignment. I verified this by removing the assignment.

        Your right. My bad. How about this for an explanation;

        The argument to int is evaluated in numeric context, which calls truncate. Then when there isn't an overload for int, 0+ is used, which calls truncate again ???