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

You seem to misunderstand the purpose of a synopsis. Your example certainly doesn't make a good synopsis so comparing it to the synopsis makes little sense to me. Was it meant to be a replacement for the synopsis? It contains too much that isn't paritcularly relevant to the summing up. To me, wading through your example looking for the important concepts doesn't seem to be much of an improvement over wading through the documentation looking for the important concepts that you were complaining about (there is less to wade through but little to point out where the important concepts are and most of the code is rather irrelevant to the key concepts).

There are already examples of the use of overload.pm that compile (as you found) so I'm not sure adding such a contrived example of that directly to the documentation is a good idea. If the point is to explain the key concepts (which seemed to be what your point was), then I don't see how being "more tutorial in nature" is anything but a good thing.

So I see more value in 1) few-line examples concentrating on fewer points and accompanied by explanatory text and 2) much less contrived complete examples in an "ex" subdirectory. An example implementation of each type of method in the section that talks about that type of method would be a welcome addition. Having to wade through your "complete" implementation in order to find such just obfuscates the key concepts (and does a poor job of documenting many of the important points of such methods and of demonstrating how to write a useful overloaded object).

- tye        

Replies are listed 'Best First'.
Re^4: Overloading by Example (pieces)
by eric256 (Parson) on Oct 10, 2007 at 23:32 UTC

    Perhaps this is all only because you are more familar with overload already? I know that when I read the documentation for overload the first few times it was worthless to me until i found a complete example. If such a complete example where found in the documentation then I know my life would have been made easier. I'm particularly confounded by modules that have code in their synopsis that can't be used as is. If i have to read the code, read the documentation and then figure out how to modify the code to get a basic sample working , then I'm far less likely to succeed in using such a module. Modules that give good self documenting examples somewhere in the documentation are much much easier to pick up and start learning. Perhaps its just a difference in learning technique, but i like to start with working code that i can futz with, not futz just to get working code.

    It probably would be better to leave any extra modules out of the example though.


    ___________
    Eric Hodges

      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