2009
05.11

So today, I ended up starting a very long discussion with several members of the Perl6 community about how functions are declared. Basically, I made several mistakes in trying to call my class functions “sub” when I meant method. Perl6 happily lets me shoot myself in the foot and then complains when I try calling the sub against an object, which doesn’t exist. Here’s a few examples of things to illustrate the clunky OO model:

Snippet 1:

class A {
sub foo {
say "never gonna happen"
}
};
my A $a .= new;
$a.foo;

Snippet 2:

class A {
method foo {
say "nope"
};
method bar {
foo()
}
}

note: the code highlighter does not support perl6

So basically, that first snippet is an error. ‘sub’ apparently means something other than what we want it to mean in this case. That snippet will fail with an error about no such method. The second snippet will fail because the ‘self’ invocant is not assumed in the bar function. jnth says it’s because they don’t want methods like push being overwritten if I define a push method in my class.

The really sad thing is that I still don’t get it. I can’t make heads or tails of this object model. In some cases, Perl6 chooses to reuse syntax correctly, and in others it fails. There are about a billion ways to define a function. Each function can be a sub, method or submethod. It can have modifiers like multi and my and attributes like does and a huge multitude of parameters. The list of function modifiers just keeps going. How anyone is going to be able to use all of this is beyond me; I doubt I’ll ever be able to make sense of it.

Okay, so my raw opinion is this: they are doing it wrong. The “many paradigms” argument is wrong because the language should do one thing and do it well. It can’t serve all masters and still do a good job. If the object model is to be a real first-class citizen, then it must place the object model above all else.

That being said, I am of the opinion that C++ is the only language to get it right (see this, this, and this). Well almost, it still can’t do runtime template binding (think variant or Boost::Any) and I loathe the ‘friend‘ keyword (breeds bad code). Namespaces and classes are different concepts, so they are treated differently. When I place a function inside of a class definition, it becomes a member function. If I prepend ‘static‘, it becomes a class method (not a Class member like Java) and I can call it without a ‘this’ reference. If I want a function outside any class, just put it at the file scope and external storage. Calling a function defined within the scope (file or member) that has been included (imported) implies the ‘this’ invocant. All parameters are required unless given default values (Perl6 has 3 different syntaxes for this: required “!”, optional “?”, default value “=”). This is simple and direct. Templates were added for an additional level of flexibility, which worked out pretty well. In fact, C++0x will improve on this, assuming it’s ever released this year (otherwise it’ll be C++1x).

In bringing this up in the channel, I was really hoping to make people think about how Perl6 is doing it in hopes that someone who knows better than I would stop and think whether Perl6′s design is ideal. I was wrong and I think I may have agitated some of the developers. The beauty of open source is that I can do things my way if I see fit and likely will once the only usable implementation has support for macros.

11 comments so far

Add Your Comment
  1. You’re missing an important linguistic principle behind the design of Perl 6: similar things should look similar and different things should look different.

    At a trivial level, subroutines and methods and submethods are the same thing in the same way that if and unless and while and do and for are all syntactic sugar for “local branch”.

    At a non-trivial level, they do different things, which is why they have different names and keywords.

    Subroutines have different visibilities and different invocation schemes and different parameter lists from methods, and similarly methods and submethods. That’s why they have different keywords, lest people have to memorize lists of rules about contexts of declaration and contexts of exporting to figure out what two identical function bodies meant.

    The method keyword is always unambiguous. Everyone who learns Perl 6 knows what it means with regard to scope and visibility and bindings and parameter lists. The same goes for subs and submethods.

    In Perl 5, you can’t tell. This is the source of many, many bugs. (If you don’t believe me, find me a time machine and you can go back in time and take over the maintenance of one of my CPAN modules which has tripped over this bug time after time after time.)

    In Perl 6, you can trivially tell, because the declarations (which have different effects) look different, per that fundamental design principle. Slightly less important than clarity of intent to readers of the code is clarity of intent to tools, such as syntax highlighters, IDEs, class browsers, refactoring systems, code generators, and more.

    Finally, your argument that not prioritizing object orientation over all other paradigms means that OO is not first class in Perl is nonsense. That’s not what first-class means in programming language design, and that’s not what it’s ever meant.

  2. To quote

    > The “many paradigms” argument is wrong because the
    > language should do one thing and do it well.”

    Sorry, Perl 6 is not the right language for you if that’s your opinion on language design. Perl always was a multi paradigm language, and will always be.

    (If you want a really nice object system (without meta object programming, though) Eiffel would be a good choice.)

    That said, I think that it’s very perlish to add new keywords and/or syntax for important features, so having a separate ‘method’ keyword is actually a sign that we take OO very seriously.

  3. I think it’s fine since (I’m pretty sure) you can just do this:

    class A {
    method foo {
    say “nope”
    };
    method bar {
    self.foo()
    }
    }

  4. Wow, three comments! I didn’t know I had any readers! Let me try to respond to you guys now.

    chromatic:

    I have a hard time believing that this would be too ambiguous in perl5. I don’t have the extensive perl5 CPAN publishing experience you do, though I have been doing my $DAYJOB in C/C++ mostly.

    I agree that different things should look different. My argument was that sub, method, and submethod wen’t different enough. That is why I recommended a method modifier, similar to how C/C++/Java does it.

    Lastly, my comment is based solely as a response to jnth’s comment about the preference of method dispatch over a little bit of OO clunkiness.

    Moritz:

    I actually like Perl6; I’m not griping or wining but hopefully trying to improve things for everyone. And yes, I fully intend on extending the STD grammar when macros are available.

    fREW:

    The point of the second example was to illustrate how annoying it was to constantly have to specify the invocant. After years of not doing that, it just seems logical that it would be implied within the class.

  5. > The point of the second example was to illustrate how
    > annoying it was to constantly have to specify the invocant.
    > After years of not doing that, it just seems logical that it
    > would be implied within the class.

    That’s clearly crazy.

    class Foo {
    method open (…) { … }
    method bar { open(…); }
    }

    If the invocant is to be assumed, then the above would call Foo’s open. But then how do I call perl’s open?

    I would guess the answer to be related to your comment about “placing the object model above all else”. One of Perl’s defining characteristics is to *NOT* slavish follow dogmatic principles because what’s important are the _programmer’s_ opinions about how problems should be solved, rather than the language designer’s. So, while the object model is important, it’s not important enough to be the lens through which all problems are viewed.

  6. > Basically, I made several mistakes in trying to call my class functions “sub” when
    > I meant method. Perl6 happily lets me shoot myself in the foot and then
    > complains when I try calling the sub against an object

    That’s a fair complaint, but it also highlights the original problem with perl 5′s reusing of subs as methods: you can’t tell that a sub is a sub and not a method.

    Maybe the compiler could check for the use of an invocant in a sub in a class and issue a warning; that would help perl 5 users migrate.

  7. >If the invocant is to be assumed, then the above would
    >call Foo’s open. But then how do I call perl’s open?

    After today’s p6m meeting, pmichaud explained in a better fashion how the sub/method thing works and it’s clearer to me. Having said that, you could always call a sub via it’s namespace, which would be IO::open() in this case.

    I fully intend on following this up tomorrow (it’s rather late).

  8. And what if I *really* want a sub and not a method?

    Maybe Perl 5->6 code translator most probably need to automatically convert some “sub”‘s to “method”, but I tend to think that Perl 5 coders should get used to write “method”.

  9. I was under the impression that received wisdom in C++ was now to prepend “this->” to all references to members of a class. The language doesn’t require it, but it helps clarify intent. I’ve been doing it for member function calls in my code for years…

  10. SF: It depends. I usually try to import as few symbols as possible into my namespace to avoid cluttering it. I haven’t run into any issues by doing that.

    That’s not to say other people may or may not do it.

  11. [...] is mostly a response to s1n’s post about subs and methods in Perl 6. He maintains that since methods are very common in classes, a sub declaration inside a class [...]