The perils of Perl 5's indirect object syntax

Perl lets you call object methods in two different ways:

  • $obj->method()
  • method $obj
The latter form is usually called only for things like clone $obj, which is pretty ugly. For more about indirect object syntax and some of the pain it causes, see When Is Backwards-Compatibility Not Worth It?.

It's funny that Max posted that blog entry the other day, because I just was shown a problem that was caused indirectly by it. Mike O'Regan showed me some code that he was surprised even compiled, because it certainly wasn't working.

sub custom_sort {
    return
        $a->{foo} cmp $b->{foo}
        ||
        a$->{bar} cmp b$->{bar}
}

See the a$ instead of $a? Yuck. But it compiles just fine. Why? Well, let's see what B::Deparse decompiles it out as:

$ perl -MO=Deparse foo.pl
sub custom_sort {
    return $$a{'foo'} cmp $$b{'foo'} || $-->a > {'bar'} cmp $-->b > {'bar'};
}
foo.pl syntax OK

Turns out that it's calling method a on the object $- and then seeing if that is greater than {'bar'}. Double-ugh.

Perl 6 still has indirect object syntax, but you must follow it with a colon, as in method $obj: @args. Larry says in Perl 6 it's completely unambiguous.

Categories:

3 Comments

With strict or warnings, the error would have been fairly obvious, though.

With strict:

$ perl -MO=Deparse -Mstrict foo.pl
Bareword "bar" not allowed while "strict subs" in use at foo.pl line 3.
Bareword "bar" not allowed while "strict subs" in use at foo.pl line 3.
foo.pl had compilation errors.
sub custom_sort {
    use strict 'refs';
    return $$a{'foo'} cmp $$b{'foo'} || $-->a > {'bar'} cmp $-->b > {'bar'};
}

Without strict, but with -w:

$ perl -MO=Deparse -w foo.pl
Unquoted string "bar" may clash with future reserved word at foo.pl line 5.
Unquoted string "bar" may clash with future reserved word at foo.pl line 5.
Name "main::b" used only once: possible typo at foo.pl line 3.
Name "main::a" used only once: possible typo at foo.pl line 3.
BEGIN { $^W = 1; }
sub custom_sort {
    return $$a{'foo'} cmp $$b{'foo'} || $-->a > {'bar'} cmp $-->b > {'bar'};
}
foo.pl syntax OK

Max said:

Wow, yeah, that's a perfect example of how these sorts of things add complexity and make it hard to parse Perl. :-) I was looking for a good example, but with some of these weird edge cases it can be really hard to come up with something unless you're an old-time perlguts hacker. :-)

-Max

Here is another, real-life example (typo I just did a couple of days ago and was wondering about the error message) : I typed curly braces instead of parentheses in

  my @list = qw/foo bar/;
  map {foobar{$_}} @list

and got that strange message

Can't locate object method "foobar" via package "foo" (perhaps you forgot to load "foo"?) 

because this is parsed as


map({do {$_}->foobar;} @list);

Leave a comment

About this Entry

This page contains a single entry by Andy Lester published on February 13, 2008 10:44 AM.

How to: Find all modules used in a tree was the previous entry in this blog.

Leopard cheat sheet is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Other Perl Sites

Other Swell Blogs

  • geek2geek: An ongoing analysis of how geeks communicate, how we fail and how to fix it.