http://qs321.pair.com?node_id=217417

In short, anyone else feel that the java/c way of 'overloading' (if thats the wrong word, oops) subs based on the number of parameters is much cleaner then the perl way, and wish that you could use prototypes to do the same thing?

Replies are listed 'Best First'.
Re: Overloading Subs in Perl
by cjf-II (Monk) on Dec 04, 2002 at 06:02 UTC

    If you want clean function overloading in Perl take a look at Class::Multimethods.

    There's also the appropriately named overload module, which I have yet to use but looks interesting.

    Update: Here's an example with Class::Multimethods:

    use Class::Multimethods; multimethod add => ('#', '#') => sub { $ans = $_[0] + $_[1]; print $ans, "\n" }; multimethod add => ('$', '$') => sub { $ans = $_[0] . $_[1]; print $ans, "\n" }; add(1, 2); add("Foo", "Bar");

    Is this what you're thinking of?

Re: Prototypes allow overloading subs?
by rinceWind (Monsignor) on Dec 04, 2002 at 10:45 UTC
    In short, anyone else feel that the java/c way of 'overloading' subs based on the number of parameters is much cleaner then the perl way, and wish that you could use prototypes to do the same thing?
    I certainly don't. It would be helpful if you could give an example of an application or a pattern where such a technique of distinct routines to handle different arglists would be useful.

    Usually, a routine or method will do a particular job, and the bulk of the code will be independent of the form of parameter passing used. The rest of the code is a wrapper to handle the different types of parameters. This is polymorphism in action - what you are suggesting is violating one of the principles and advantages of OO. In Perl, rather than true polymorphism, most people adopt DWIM when designing the interface.

    When it comes to perl's prototypes, I avoid using them, as they are nearly always more trouble than they are worth. I prefer the freeform arglist array that perl gives you in @_, and if I need something complicated, I cast (copy) it to a hash to implement named params.

    Also, what I've just said applies to Perl 5. Perl 6 promises to provide much better ways of handling argument lists than Perl 5's prototypes.

      rinceWind wrote:

      It would be helpful if you could give an example of an application or a pattern where such a technique of distinct routines to handle different arglists would be useful.

      Curiosly, I'm working with a similar issue, so here's an example. Let's say I want to create a 'people' object and save it in the database.

      my $person = People ->new ->first_name( 'Tommy' ) ->last_name( 'Atkins' ) ->email( 'dev@null.com' ) ->save; my $id = $person->get_id;

      The above works if all mutators return the object (typically $self) instead of true.

      Now let's say that later on I want resurrect poor Tommy Atkins from his database internment. Assuming I have his $id, I can do this:

      my $person = People->new( $id ); print $person->first_name, ' ', $person->last_name;

      In the first case we call the constructor with no argument and set all of the data by hand. In the second case we call the constructor with an ID and get what we need. This is method overloading (though I realize we're talking about function overloading, I think the point is the same). In many OO languages, you would code entirely different methods. Let's consider Java (I'm leaving a lot of stuff out here):

      class People { // Java constructors have the same name as the class // People person = new People(); // People person = new People(id); public People() { // initialize empty object } public People( int id ) { // we can now access an object by id } }

      The line public People( int id ) is called the method signature. By allowing the code to dispatch to different methods based upon different signatures, you simplify the logic tremendously. For Perl, without using an external module to fake it, you wind up with something like this:

      sub new { my ($class,$id) = @_; my %data; %data = $class->_initialize($id) if $id; bless \%data, $class; }

      That isn't too complicated, but many people will stick all of the logic in one method or try to do method dispatch based on the number and type of arguments. Further, the more argument types you have to deal with, the more complex this becomes. By forcing the programmer to use extra logic, you increase the chance for bugs. Method overloading on signatures reduces the amount of logic necessary and therefore reduces the amount of bugs and makes the code simpler.

      As a side note, I generally prefere explicit 'get' and 'set' methods rather than overloading those methods. It makes the code easier to understand and doesn't confuse the programmer when they try to do something they shouldn't be able to do.

      # no! If this is mapping to a database, you probably don't # want them to be able to change the id $person->id( $id );

      With the above, it may silently fail. Instead, when the programmer tries to call set_id, they'll likely get a useful error message letting them know that this method is not available.

      Cheers,
      Ovid

      New address of my CGI Course.
      Silence is Evil (feel free to copy and distribute widely - note copyright text)

        In my opinion the perceived "need" for this feature comes from too much attachment to the perfectly ordinary method name "new". Once you realize that not all factory/constructor methods have to be called "new", things become much clearer.
        my $person = People->new()->etc... ... my $himagain = People->restore($id);
        Other useful constructor names can be "new_from_file", "new_named", etc. That way, particularly when the arguments are called $a, $b, $c, you'll have documented what the (sometimes quite substantial) differences are between all the overloaded "new" methods.
      It would be helpful if you could give an example of an application or a pattern where such a technique of distinct routines to handle different arglists would be useful.
      Here's an easy one off the top of my head:operator overloading. Not often done, but useful. You would, for example, want to handle multiplication differently if the left and right sides of the * were both matrices then you would if the left side was a matrix and the right a scalar. (In the first example you'd probably do a full matrix multiply, and in the second you'd probably multiply each element of the matrix by the scalar. Probably...)

      Any time you have a sub that checks the passed parameters and then dispatches to different code depending on the type or quantity of the parameters you're a good candidate for this. It's much less error prone (and usually faster) if the language handles the dispatch.

Re: Prototypes allow overloading subs?
by dbp (Pilgrim) on Dec 04, 2002 at 10:01 UTC
    I've also had a need for this sort of functionality although not necessarily in an OO context. For example, say you want a sub to be called in a manner similar to the built-in sort (most likely in a module). This example (toy) code throws an error:
    #!/usr/bin/perl -w use strict; sub func (&@) { my $func = shift; foreach (@_) { $func->($_); } } sub func (@) { foreach (@_) { print $_; } } my @list = ('bob', 'sally', 'joe'); func { print "!!!$_\n"; } @list; func @list;
    It would be quite nice to be able to do this sort of thing. If you didn't need the prototype to treat a block as a sub things would also work out. It is also easy to work around this issue by taking an explicit reference to a sub but the block is much more natural and provides subs that are similar in use to the built-in functions. Perhaps I am missing something obvious...
Re: Prototypes allow overloading subs?
by Dog and Pony (Priest) on Dec 04, 2002 at 14:48 UTC
    Well, yes, sometimes. Especially since I see so much code trying to emulate this behaviour instead, checking if the first argument is a hashref, do this, if it is an arrayref, do that, if it is a coderef, do that etc.

    Technically it would not be so much of a difference in most cases, codewise, if you had prototypes (*not* the normal Perl prototypes) do this, as most such overloaded functions do some quick reformatting and then passes the data on to the next in line. So it is really the same, but it is cleaner and easier to read/write. For those that are used to it. :)

    But I don't really like that feature when I don't have default arguments to go with it (like C has and Java don't). So that would have to be included, because even though the workaround is simple, it is annoying. :)

    Apart from the multimethod module suggested above (which looks pretty cool too), I guess one could do some "preprocessing" magic with a Filter module or something as an alternative. Like I said, having that kind of methods is very similar to do typechecking inside one function instead, so a source filter could probably accomplish this. If someone is bored enough to write it. :)


    You have moved into a dark place.
    It is pitch black. You are likely to be eaten by a grue.
      My immediate thought when you suggested the source filter module, is that it wouldnt be that hard, as you could just steal the method used by c compiler things (if i recall correctly...) that internally turn say "baz(int,int)" into "baz_int_int()", then when you do like "baz(1,1)", it knows how to pass it. The first step seems relatively simple, rewriting sub like "sub foo($,@)" into "sub foo_scalar_array", but that breaks down when you try to determine what the hell the sub is being passed. @x=(1,2); add(@x)?
        Perl is just too dynamic. The biggest obstacle is that you can often not know whether you're getting a list or scalar - much like in your example. It gets a whole lot worse when you say something like add(numbers($foo)) and numbers() can return anything from one invocation to the next.

        Makeshifts last the longest.

Re: Prototypes allow overloading subs?
by alien_life_form (Pilgrim) on Dec 04, 2002 at 14:15 UTC
    Greetings,

    Mmmhhhh...
    Not the java way for sure. If anything, I'd go the python way:

    def foo(one,two=None,*args,**kwargs) : pass
    So I can say:
    foo(1) foo(1,2) foo(two=3,one=4,'fubar',three=42)
    While perl's way may be too free form sometimes, trying to work around - for instance - C# idea of not allowing default values for arguments is a royal pain in the backend.
    Cheers,
    alf
    You can't have everything: where would you put it?