Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

The beauty of MMD

by Ovid (Cardinal)
on Jul 28, 2005 at 17:06 UTC ( [id://479051]=note: print w/replies, xml ) Need Help??


in reply to Re^2: Perl 5's greatest limitation is...?
in thread Perl 5's greatest limitation is...?

tilly wrote:

You have not convinced me that designs that rely on MMD are a good idea. Particularly not in a language like Perl where the way people usually use the language will lead to people expecting you not to do that.

Hmm, that's a rather interesting way of putting it. Let's play with the wording a bit and see if we can clear that up, shall we?

You have not convinced me that designs that rely on closures are a good idea. Particularly not in a language like Java where the way people usually use the language will lead to people expecting you not to do that.

While certainly not an exact quote, that's pretty darned close to a rebuttal a Java programmer gave me in response to my arguments about the merits of closures. No offense Ben, but those two sentences are orthoganal to each other. Just because people aren't used to X doesn't mean that X is not a good idea.

Now that we have that out of the way, let's consider MMD. MMD should not be overused, but when it's necessary, it simplifies the code and is a better solution than a bunch of similarly named methods or forcing the programmer to write code to simulate it. However, MMD and proper argument processing go hand-in-hand, so much of the following assumes we're talking about both.

For example, if you look at how dispatch tables are frequently implemented in Perl, you'll find that they're often used to dispatch on type. Thus, the programmer has to write some code similar to this:

my %dispatch = ( Foo => \&_foo, Bar => \&_bar, Baz => \&_baz, ); sub process { my ($self, $thing, $data) = @_; my $type = ref $thing || ''; my $method = $dispatch{$type} || die "Can't find method ..."; $self->$method($data); }

That, of course, it ugly, but it's a lot less ugly than the faux switch statements or if/elsif/else chains people come up with (and less error prone, too). To compare with MMD:

multimethod process (Foo $thing, String $data) { ... } multimethod process (Bar $thing, String $data) { ... } multimethod process (Baz $thing, String $data) { ... }

Not only is that shorter, it's more likely to be correct. Forcing me, the programmer, to write code to handle a common idiom that so many modern languages never have to worry about is silly. Heck, how many times have we seen the following bug?

sub name { my ($self, $name) = @_; $self->{name} = $name if $name; return $self->{name}; }

Now, if you need to clear someone's name, it's very difficult to do. That bug is virtually impossible to write when using MMD. What a beautiful thought. An entire class of bugs eliminated. (Of course, MMD introduces a completely different class of bugs, but in my experience, they bite far less often.)

And let's not forget that MMD potentially allows compiler optimizations that aren't possible when dealing with all of the potentiall buggy programmer alternatives. And while we're on the topic of optimizations, notice that traditional Perl code requires two sub calls for the dispatching instead of one. Perl's subs are slow enough that this could be a significant performance bottleneck on some systems. I've been hit by this and working around it can be very painful.

However, let's take a look at my dispatch code. If I want to add an extra event, I add another entry to the dispatch table. But what happens when someone does this?

$object->process($foo, $data1, $data2);

Oopsie. The code silently fails. Perl's poor argument handling strikes again! However, if I need to actually allow that, my dispatch code can get awfully ugly. With MMD, that can easily throw a nice, fatal error, letting the programmer know that no such method exists. Otherwise, I just write another method. Nice and easy either way.

And while we're thinking about argument handling, I see (and have written) plenty of code which doesn't verify the number arguments to a method. In fact, this is a debate that regularly comes up in the Perl community: how much sanity checking is required? Many hackers do very little checking and, if we're really lucky, they have test suites to catch stuff. Some put sanity check everything and then moan in horror about how slow their system is.

Guess why Java programmers don't debate this too much? Because, ironically enough, in the case of MMD and proper argument handling, they have the language doing something that the programmer shouldn't be forced to do.

PS: When you show up for OSCON, let me know and I'll buy you a beer. Or you can buy me one. Or four. I know how to dispatch them properly.

Cheers,
Ovid

New address of my CGI Course.

Replies are listed 'Best First'.
Re: The beauty of MMD
by tilly (Archbishop) on Jul 28, 2005 at 22:34 UTC
    First of all, as was already pointed out, I am not arguing that multi-methods are necessarily bad. Just that you have not convinced me. I further went on to say what would be required to convince me. The fact that this argument can be used by someone else with regards to something that I am convinced is good is not a refutation of that form of argument. No number of examples of people with mistaken opinions will convince me that it is a bad idea for people to try to form their own opinions. In fact no number of examples where I have had a mistaken opinion will convince me that I shouldn't try to have my own opinions!

    Now I see what you're saying in your example, but I have several major caveats.

    First of all let's separate the need for a dispatch mechanism from the desire for multi-method dispatch. Much of what you've just said for multi-method dispatch by type works perfectly well already in Perl if you're dispatching by class using OO. Arguing for how great multi-method dispatch would be using examples where OO dispatch suffices is not going to convince me. You don't need to convince me that there is a need for built-in dispatch mechanisms. You need to convince me that there is a need for complex built-in dispatch mechanisms.

    Remember that I already suspect that a basic maintainance problem with heavily factored OO code is that there is an implicit if in every method call, making the complexity of OO code higher than procedural code of similar length. I am concerned that making the dispatch potentially more complex makes the complexity higher still. In particular I had this concern from descriptions of Perl 6 has had rules to try to figure out a type if nothing matches exactly. When I read descriptions of how it does that, I'm often left wondering how often it would wind up dispatching somewhere I don't want it to dispatch, and how often I would want an error but wouldn't get one.

    Now you used one example where it seems to make sense to dispatch based on number of arguments. And that is an accessor which plays setter and getter. However I have another solution to that problem which I like a lot. And that solution is name your setters and getters differently! A really cute idiom for this in Ruby does like this. If you name your getter bar, then you name your setter bar=. Then Ruby has a nice piece of syntactic sugar where you can write foo.bar = baz; and it automatically knows to call your bar= method. (Ruby has another nice piece of syntactic sugar where it will autogenerate acceptable default accessors for you very easily, but I digress.)

    There is a basic choice here. You can either try to have a complex dispatch method to allow you to overload a method with many different meanings. Or you can have a straightforward dispatch method and have a number of similar (but very distinct!) methods. My natural inclination is towards the latter. (This would be one of the reasons that I am not a fan of multiple inheritance...)

    Now lets move on to hand-rolled dispatch mechanisms. As I've already noted, Perl has a working OO dispatch mechanism. Therefore if I write a hand-rolled dispatch mechanism, my needs are likely to be rather customized. For one thing I'm almost always dispatching on value. Secondly I sometimes have a customized dispatch table - you can pass the dispatch table into the function. Thirdly I frequently want to customize the "fallback" behaviour. Fourth I usually have a non-trivial lookup to figure out what I'm dispatching on.

    Now I see how a smart MMD system could satisfy some of those needs. But I don't think (I may be mistaken, of course) that proposals for Perl 6's MMD system would be flexible enough to handle the cases where I want to write a customized dispatch system in Perl 5.

    Now let's wrap up loose ends.

    The optimization note doesn't matter to me. Perl is simply not a bottleneck for the kinds of things that I want to do with it. (Usually the bottleneck is the database.) And if I really wanted performance, Perl wouldn't be the tool that I'd reach for. That isn't about to change any time soon.

    Please don't take any of what I've said as a slam on Perl 6. My understanding of Perl 6 says that I can ignore whatever I want to ignore. I'm fine with not using a capacity for MMD.

    And about the PS, I'm not only hoping to exchange a beer or three, I'm hoping that you have some good ideas on where to find the best parties. :-)

      tilly wrote:

      Much of what you've just said for multi-method dispatch by type works perfectly well already in Perl if you're dispatching by class using OO. Arguing for how great multi-method dispatch would be using examples where OO dispatch suffices is not going to convince me.

      I'm not sure I follow you here. Are you suggesting that in examples like I provided, the invocants be in different classes? Whenever possible, I really like to minimize the number of classes and creating a new class just to handle a different argument type to a method frequently does not make sense. One example of this would be the constructor:

      my $parser1 = Some::Parser->new($string); my $parser2 = Some::Parser->new($uri_object); my $parser3 = Some::Parser->new($fh_object);

      In that example, with three constructors, each taking a different argument type, we can still return objects in the same class. Class-based dispatching wouldn't make sense there. Did I misunderstand what you were saying?

      Dan Sugalski points to event driven code where different objects generate events and your event handlers are a series of methods whose arguments are the objects generating the events. I shouldn't push the event handling into the objects themselves because they shouldn't care how they're going to be used. Instead, I have a simple GUI class which knows that it's supposed to handle events. This is a common Java idiom and it works very well.

      The main problem I have with MMD is I am frequently more interested in the interface an object provides rather than its type/class.

      Oh, and you'll see that I'm not the one who used the getter/setter example. While it's a reasonable example that fits well with many people's coding styles, I also prefer different names there because the methods are doing different things. When the methods are conceptually doing the same thing, they should have the same name, if possible.

      Update: Whoops! I guess I did use a getter/setter example :)

      Cheers,
      Ovid

      New address of my CGI Course.

        As for the invocants being in in different classes, that is indeed what I suggested. Like LWP does with the LWP::Protocol::* classes. This solution has pros and cons, with the need for appropriate classes to exist being one of the cons. But if we're going to rule out a design technique because it requires us to create a few extra classes, then we would never use MVC. Sometimes it is not worth adding classes, but if it makes sense, don't hesitate.

        Speaking of MVC, Dan Sugalski's prolems seems to me to be one that you can solve with an MVC design. In fact people happily do so in languages like Smalltalk and Ruby. Yes, it does mean designs with some extra redispatching. I don't consider that a major flaw.

        As for your Some::Parser example, I grant that a direct class-based dispatch doesn't work there. However it is trivial to extend it in ways where a direct MMD dispatch won't work either. For instance suppose that you're parsing any of several related formats and need custom logic to autodetect which format you've been presented with. If you're getting that specialized, it makes sense to me to have customized dispatch logic. Not every facility needs to be built into the language, and people have been writing parsers with customizable behaviour since before either of us started programming.

        I agree with you about interfaces being more important than types and classes. And that's one of the reasons that I have no problem writing small helper classes whose job is to do the translation so that different kinds of things can present the interface that you want.

        And I should point out that you did use the getter/setter example. I direct you to your implementation of name in The beauty of MMD.

Re: The beauty of MMD
by jdporter (Paladin) on Jul 28, 2005 at 18:31 UTC
    Let's play with the wording a bit and see if we can clear that up, shall we?
    If we're going to play that game, I'll have a go:
    ... when it's necessary, it simplifies the code and is a better solution ...

    Seems to me that in cases where it's necessary, it doesn't just simplify the code and make a better solution — it makes a solution (and the code) possible. That's what "necessary" means.

    More to the point, you completely ignored the part where tilly said (and which I second):

    To be convinced I'd need to see a problem which interests me that I have trouble solving without MMD, which has a very nice solution using MMD ...

    (Aside: why did you call tilly "Ben"? Seems to me that is just making the argument unnecessarily personal — sort of a virtual "grabbing your opponent by the lapels".)

    But anyway...

    MMD potentially allows compiler optimizations that aren't possible when dealing with all of the potentially buggy programmer alternatives.
    What compiler optimizations are you thinking of? Seems to me there are precious few possible compiler optimizations in dynamic languages relative to their static brethren.
    $object->process($foo, $data1, $data2);
    Oopsie. The code silently fails.

    That is an argument for prototypes on methods, not for MMD specifically.

    This is a debate that regularly comes up in the Perl community: how much sanity checking is required?

    I fall in the camp that believes perl should stay out of my way unless and until I ask otherwise. Perl is not a B&D language. Strict is off by default.

      First, I called him "Ben" without thinking about it. He happens to be a friend of mine, we talk on the phone and calling him Ben seems to be natural. It wasn't intended as making thing "personal."

      As for the rest of your points, argue them with Dan Sugalski :) Incidentally, that link points to a concrete example of something that might satisfy tilly's desire to see a problem for which MMD is suitable.

      Oopsie. The code silently fails.

      That is an argument for prototypes on methods, not for MMD specifically.

      As I pointed out earlier in my post, my discussion was about both MMD and proper argument handling. To my mind, you can't have the former without the latter. And I agree with your concerns about B&D. I, too, don't like B&D, but to my point of view, properly implemented MMD could let me having sanity checking without forcing any sort of B&D. See Perl 6's ideas behind MMD for examples.

      Cheers,
      Ovid

      New address of my CGI Course.

      Jumping into a fray that isn't mine:

      I think that the point is MMD is usefull and would be nice to have. I'm not sure what your argument agianst that was except that you didn't like his argument. So here is a plain as day example using Perl6 MMD and i'd like to see your example of the same code that is at least as simple.

      multi method name () { return $.name }; multi method name (Str $new_name) { $.name = $new_name; $new_name };

      The return value of the second call is always debatable, but not realy the point here. This is obviously a very simple example, but the point is, anytime your action varies depending on the types of paramters, MMD is a nice trick to have available. BTW Perl6 allows MMD and allows you to still have your old fashion perl5 send me everything in @_ subs too, so you can have your cake and eat it too (whatever that means ;) )


      ___________
      Eric Hodges
        Here is a Perl 5 example that is simpler than what you provided, even without the Perl 6 syntactic sugar:
        sub get_name {$_[0]{name}} sub set_name {$_[0]{name} = $_[1]}
        And in Ruby it is simpler still:
        attr_accessor :name; # creates name and name= methods for you
        The one thing that my examples are missing which yours provides is a check that the accessor really wants a string. This check is not something that I personally value, but YMMV.
        Off-topic comment on "have your cake and eat it too" below.
        A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://479051]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (5)
As of 2024-03-29 11:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found