in reply to Re^4: Overloading by Example (pieces)
in thread Overloading by Example

I'm not sure how a presumed familiarity on my part makes sense as explaining why I prefer a more tutorial approach. I just don't find the example provided to be very instructive and don't see how the average stranger to the module would make that much sense out of it. And the module is complex enough that any example that is easy to understand is going to be quite incomplete (certainly inappropriate for a synopsis).

I also hate examples that leave me wondering "what is the point?". For many modules, I read the documentation and still have no idea why anybody would want to use them. Defining a class that takes a number such that I can use it to add that number to another number is a perfect example of a "what is the point?" class. The original example provides me absolutely no motivation for using a complex class instead of just using a simple number directly. That example doesn't demonstrate there being any point to overload.pm.

Thanks to syphilis for noting that just doing stringification makes for a much simpler example that can be made much clearer. So I'd augment the overload.pm documentation similar to below. Thanks to syphilis, it can start with a self-contained example.

It isn't very word-smithed, just a bit of glue for the examples.

The point of overload.pm is to allow you to have perl call your custom code when certain operations are performed on your objects. So you can declare that, for objects of your class, when the object is used as a string, call this code to get the string value to use. Or you can define what it means for $obj1 == $obj2. Sometimes it even makes sense to define arithmatic-like operations on your objects so that $obj1 + $obj2 calls your own method.

Note that to "override" an operation means to replace it unconditionally. To "overload" an operation means that the operation will do one of two or more different things depending on what types of operands are used with the operator.

So most of our examples will be based on the following very simple object, a "tuple", that is, a simple (ordered) list of values. You can use a tuple as the coordinates of a 2-dimensional or 3-dimensional point, for example.

package Example::Tuple; use overload( '""' => \&asString, ); sub new { my( $class, @coords )= @_; return bless \@coords, $class; } sub asString { my( $self )= @_; return "(" . join(",",@$self) . ")"; }

So, with that example, you can use a tuple like:

require Example::Tuple; my $origin= Example::Tuple->new( -3, 5 ); my $dest= Example::Tuple->new( 2, 1 ); print "Now draw a line from $origin to $dest.\n";

which produces the following output:

Now draw a line from (-3,5) to (2,1).

Now, when you get to the discussion of boolean operations, you add to the example:

Now let's define what it means for two tuples to be equal. We can do that with either eq or ==. We'll leave == for testing "are these the same object" and use eq for "are these tuples equivalent".

package Example::Tuple; use overload( 'eq' => \&isEqual, ); # ... sub isEqual { my( $self, $arg, $isReversed )= @_; # We ignore $isReversed since ($x eq $y) is also ($y eq $x) return 0 # Tuples of different sizes are never equal if @$self != @$arg; for my $i ( 0 .. $#$self ) { return 0 # Tuples differ if any paired elements differ if $self->[$i] ne $arg->[$i]; } return 1; # Otherwise they are equal. }

So now you can do things like:

require Example::Tuple; my $w= Example::Tuple->new( 3, 4 ); my $x= Example::Tuple->new( 4, 3 ); my $y= Example::Tuple->new( 3, 4 ); my $z= $w; print "w and z are the same object.\n" if $w == $z; print "$w and $y are at the same position.\n" if $w eq $y; print "$x and $y are at different positions.\n" unless $x eq $y;

Later we'll show how to make it so $x ne $y is automatically figured out based on how you defined $x eq $y.

Then you show an example where order matters. Drat, I thought that this would be easy to do with tuples but all of the examples I come up with either order doesn't matter or there would not be a reason for the method to be called with reversed arguments. But tuples are nice because you can define (w,x)+(y,z) as well as m*(x,y). You can even define (w,x)*(y,z) to mean "dot product" so that $point*$point gives you the square of the distance between the origin and $point.

Perhaps the example could be "foo".$tuple vs $tuple."foo":

package Example::Tuple; use overload( '.' => \&concatString, ); sub concatString { my( $self, $string, $isReversed )= @_; my @new; for my $elt ( @$self ) { push @new, $isReversed ? $string . $elt : $elt . $string; } return __PACKAGE__->new( @new ); }

Anyway, then you can combine all of the example code into a real Example::Tuple module that is included in the distribution (usually these examples go in an "ex" subdirectory and don't get installed -- but the example being a module itself complicates the choices here). Or maybe the examples should be based on Math::BigInt or a simplified version of it and people can look at the production version of the module to see the examples fully combined and even see what practical problems caused changes over time.

Maybe my Math::BigApprox is an even better candidate for such examples, since most of the methods are very simple.

So I hope that clarifies why I think more than one example is needed and that most of the examples shouldn't try to be fully self-contained but instead concentrate on demonstrating just a few concepts that are important in that section of the documentation.

I'd certainly leave the synopsis of overload.pm as incomplete code that very briefly shows minimal examples of each type of feature in the module, including the simple functions even though they are rather "lesser" features of the module. Though the synopsis could be improved a bit, of course.

- tye