in reply to Re: Re: perl6 & OO
in thread perl6 & OO

Your first example shows a limitation with Perl. By being forced to use a conditional to deal with the number of arguments, we automatically introduce more chances for bugs because every conditional is an opportunity for bugs to arise. The fewer conditionals you have, the fewer bugs you're likely to have. Obviously you can't avoid them, but fewer should be needed with proper OO design. With Java, the method called is automatically the one you want. Not so with Perl. What happens when you want to handle one, two, or three arguments with different behavior if the optional third argument is a string or a float? In Java, that's four methods. In Perl, typically it's an ugly if/else construct with the logic handled in the method or then dispatched to to the other four methods.

As for the case of foo(@bar), Java wouldn't handle that the way a Perl programmer is expecting because it doesn't turn that array into an argument list. Instead, it would dispatch it to a method that takes an Array object. To simulate that in Perl, create an array object or pass the array by reference.

This also brings up a strength of Perl that's a limitation in Java and many other languages. In Perl, are functions and methods are implicitly variadic (they take a variable number of arguments). If you code your methods right, this can be useful, but it can also mean that arguments fall off the end.

Because of Perl's behavior, strange bugs are available. Consider this:

sub foo { return @foo } $object->bar($wibble, foo());

Some people might be fooled into thinking they're passing at least two arguments. However, if @foo is empty, then &foo will return an empty list and when the list is flattened into the argument list of &bar, you'll only have one argument:

$ perl -MData::Dumper -e 'sub foo {@a}; bar(1,foo()); sub bar {print D +umper \@_}' $VAR1 = [ 1 ];

Again, I love Perl, but the lack of useful prototypes can really hamper the language at times.

Cheers,
Ovid

New address of my CGI Course.

Replies are listed 'Best First'.
Re: Re: Re: Re: perl6 & OO
by Anonymous Monk on Feb 21, 2004 at 23:44 UTC
    Your first example shows a limitation with Perl. By being forced to use a conditional to deal with the number of arguments, we automatically introduce more chances for bugs because every conditional is an opportunity for bugs to arise.
    I have two problems with this: 1. what's going to cause more errors, a condition or copying whole methods? 2. if a method does different things with different arguements, why is it called the same thing?
    The fewer conditionals you have, the fewer bugs you're likely to have.
    The less code you have the fewer bugs you're likely to have surely?
      I have two problems with this: 1. what's going to cause more errors, a condition or copying whole methods? 2. if a method does different things with different arguements, why is it called the same thing?

      Those are both dealt with by proper programming. I'll deal with the second question first. If you have two methods that do different things, they should probably be named differently. However, if they do conceptually similar things but act on different types of arguments, then giving them the same name can make for code that's easier to understand. A common (but perhaps too trivial) example used for demonstration would be a "Geometry" class. If you want to find out if and where two things intersect, but those two things could be points and lines, you have several possible ways the arguments could be arranged:

      1. point, point
      2. point, line
      3. line, line
      4. line, point

      In the above list, items two and four might be the same thing if you merely reverse the order of arguments. Internally, it become something like this:

      point intersects(Line line, Point point) { return lp_intersect(point, line); } point intersects(Point point, Line line) { return lp_intersect(point, line); }

      Whether or not two points intersects is merely a check for equality (the following example assumes that equals() returns a point object):

      point intersects(Point point1, Point point2) { return point1.equals(point2); }

      However, where two lines intersect is a simple equation and is handled slightly different from the above cases, though conceptually it's the same thing.

      The "point and line" argument pair addresses your first concern. In that case, we don't "copy whole methods" (your first issue) because the actual calculation is done in the lp_intersection method. How might this be done in Perl?

      sub intersects { my ($self, $arg1, $arg2) = @_; unless ( ($self->_is_point($arg1) or $self->_is_line($arg1)) && ($self->_is_point($arg2) or $self->_is_line($arg2)) ) { die "Bad args"; } if ($arg1->isa('point') and $arg2->isa('point')) { return $arg1->equals($arg2); } elsif ($arg1->isa('point') and $arg2->isa('line')) { return $self->_lp_intersect($arg1, $arg2); } elsif ($arg1->isa('line') and $arg2->isa('point')) { return $self->_lp_intersect($arg2, $arg1); } elsif ($arg1->isa('line') and $arg2->isa('line')) { return $self->_line_intersect($arg1, $arg2); } }

      That is much more difficult to read through. Also, the extra argument validation check is not built in to the language, but must be done manually and this makes it more difficult to maintain. What happens when you want to add a plane to that? The validation block becomes unweildy:

      unless ( ($self->_is_point($arg1) or $self->_is_line($arg1) or $self->_is_p +lane($arg1)) && ($self->_is_point($arg2) or $self->_is_line($arg2) or $self->_is_p +lane($arg2)) ) { die "Bad args"; }

      That becomes tedious enough that many Perl programmers simply skip it. One might argue that this is just bad programming, but a programming language shouldn't encourage bad programming. With proper method signatures, this argument checking is merely a matter of specifying the argument signature to the method.

      To further this, what happens if you type the following for validation?

      unless ( ($self->_is_point($arg1) or $self->_is_line($arg1) or $self->_is_p +lane($arg1)) && ($self->_is_point($arg2) or $self->_is_line($arg2) or $self->_is_p +lan($arg2)) ) { die "Bad args"; }

      Do you see the typo? If the second argument is rarely a plane, you might not find out until the program has been running a while. What happens if you have proper signatures?

      point intersects(Point point, Plan plane) {...}

      That won't even compile. It won't be a mysterious run-time error. Of course, this does point (hah!) out an issue that some might not like with Java: you can't overload methods based upon return type. Returning a point when you have two planes (and in the special case of where a line falls completely in a plane) does not make sense, but in Java, you can't do this:

      line intersects(plane, plane) {...}

      This is when many would argue that a separate method name is truly justified because the return type is different. Personally, I'm not so sure, but I don't program Java enough to really have an opinion.

      None of this is to say that either Perl or Java is a better language. However, there are trade-offs involved and a programmer is well-served to know the limitations of the language that she is working with.

      Update: There are a couple of caveats regarding the Perl validation that I should have pointed out. First, it will break if I'm not passing in objects. Thus, using UNIVERSAL::isa($object, $type); is preferred. Second, some might argue that I'm making things too complicated because if each type inherits from a common "Geometry" class, then the validation can be reduced to this:

      die "Bad args" unless UNIVERSAL::isa($arg1, 'Geometry') && UNIVERSAL::isa($arg2, 'Geometry');

      This seems cleaner but the method fails silently if I pass in a Cone or a Sphere (assuming, reasonably, that those are also 'Geometry' objects). Also, I wind up specifying a particular base class and this is not good. In this example, it's probably harmless, but what if I'm just worried about the interface an object presents and do not care about its class? An isa() check is even less reasonable.

      Also note that, because I was typing that quickly before I had to leave for work that I simply added "Bad args" error message. That's next to useless, but it requires more programming to add robust, easy to understand error messages. Since we don't have compile time argument checking in Perl, this is just so much more "make work" in the language.

      Cheers,
      Ovid

      New address of my CGI Course.