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

Bod has asked for the wisdom of the Perl Monks concerning the following question:

Over on on another thread, I got my wrist slapped* by kcott for using Indirect Object Syntax. I have read the linked documentation and its warnings and I am not sure I totally understand. So I am hoping some wise Monks will help with clarification.

The offending code I posted was:

my @bounds = new GD::Image->stringFT($colour, "Image/outline.ttf", 90, + 0.18, 0, 0, $watermark_text);
As I read the documentation, the problem is that the Perl interpreter has difficulty knowing whether I mean:
@bounds = &new(GD::Image->stringFT(...));
or
my $gd = new GD::Image; @bounds = $gd->stringFT(...);
(&new used to make it clear it is a subroutine!)
and because Perl's interpreter could potentially get this wrong, so could any human trying to understand the code.

Is that about right???
Or is there more too it than that?


A secondary question that follows on...
The documentation says:
To parse this code, Perl uses a heuristic based on what package names it has seen, what subroutines exist in the current package, what barewords it has previously seen, and other input. Needless to say, heuristics can produce very surprising results!

Does this mean that a constant piece of code, such as a module, could behave very differently depending on context? For example, if the same module were utilised in two different scripts? And what about between different versions of Perl. Could code behave differently depending on the version of Perl?

* Just to be clear, it was a very welcome wrist slapping from kcott - I am here to learn and hopefully help others. I am always very grateful when my mistakes are pointed out as it allows me to improve my skills.

Replies are listed 'Best First'.
Re: Indirect Object Syntax (Parsing Barewords References)
by eyepopslikeamosquito (Archbishop) on Nov 06, 2021 at 22:50 UTC
      see also Indirect Object Syntax by Bod from (working class) Coventry, UK.

      Around here they call me posh as I hail from Warwick. Only 14 miles distance geographically but a world apart if the British class system still existed!

      new Foo has two barewords

      Is new not a keyword that Perl understands without any context other than a module name will follow?

      So, to ensure I do understand...
      Are these the accepted way to do it? Can they be imporved?

      my $gd = GD::Image->new(); @bounds = $gd->stringFT(...);
      Is it OK to condense that down to this and avoid the intermediate variable definition?
      @bounds = GD::Image->new()->stringFT(...);
      Are the brackets necessary as the -> operator explicitly tells Perl this is a method call?
      @bounds = GD::Image->new->stringFT(...);


      Update: I've been reading this book and realised that new is not a keyword in Perl! It is just the name of a commonly used module method. Another little chunk of understanding falls into place...

        I've been reading this and realised that new is not a keyword in Perl!

        Correct! Though it's generally considered polite to pay for those books than to read the pirated versions online (you may want to remove the link and replace it with a reference to the actual book).

        Are these the accepted way to do it? Can they be imporved?

        Yes, those are (syntactically) correct and best practice. They can be improved in the ways you showed:

        Is it OK to condense that down to this and avoid the intermediate variable definition?

        Yes, if you don't need the object - though some OO classes make a habit of returning the original object from some methods in order to explicitly allow method chaining, e.g. my $json = JSON::PP->new->ascii->pretty->allow_nonref;

        Are the brackets necessary

        No, they're not; IMHO it's fine to leave them off when there are no arguments (others might feel differently).

        > Around here they call me posh as I hail from Warwick. Only 14 miles distance geographically but a world apart if the British class system still existed!

        Good to know you share a nickname with Posh Spice. :) We have something in common because my mother also hails from Warwick - only 10,286 miles distance geographically from Warwick UK, but a world apart! Unlike its UK namesake, Warwick Queensland is definitely not posh. :)

        Is new not a keyword that Perl understands without any context other than a module name will follow?

        Technically, one can use any valid sub name instead of new for the role of "create object" (i.e. blessing a hash with the module name), when writing an OOP module -- and can use sub new for some other function, confusing yes but it shows that name new is nothing special:

        { package MyObj; sub retro { bless { name => $_[1] }, $_[0] } sub string { join ', ', values %{ $_[0] } } } my $obj = MyObj->retro("aghag"); my $obj2 = retro MyObj("aaa"); # with the usual caveats

      I don't think there's any guesses. If there is, let me know so I can fix this.

Re: Indirect Object Syntax
by choroba (Cardinal) on Nov 06, 2021 at 20:48 UTC
    Short demonstration of the problem:

    #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; sub new {} { package MyObj; sub new { bless { name => $_[1] }, $_[0] } sub string { join ', ', values %{ $_[0] } } } say 'MyObj'->new('obj1')->string; say new MyObj('obj2')->string;
    This works as expected.
    obj1 obj2

    Now try moving the package definition to the end of the code.

    obj1 Undefined subroutine &main::MyObj called at ... line 9.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Indirect Object Syntax
by LanX (Saint) on Nov 06, 2021 at 20:13 UTC
    > my @bounds = new GD::Image->stringFT($colour, "Image/outline.ttf", 90, 0.18, 0, 0, $watermark_text);

    it's never a good idea to mix two syntax variations

    use my @bounds = GD::Image->new->stringFT($colour, "Image/outline.ttf", 90, 0.18, 0, 0, $watermark_text);

    Perl wanted to provide maximum flexibility by allowing method syntax from two different language families, this leads to very annoying error messages.

    is this

    foo bar

    • sub foo(bar()) or
    • bar->foo
    ?

    IMHO indirect object syntax is a good candidate for deprecation.

    It could be that only new , print and say will survive.

    Avoiding is best, and mixing is a no-no!

    update

    More in Ch15 of PBP

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      my @bounds = new GD::Image->stringFT($colour, "Image/outline.ttf", 90, + 0.18, 0, 0, $watermark_text);
      it's never a good idea to mix two syntax variations
      use my @bounds = GD::Image->new->stringFT($colour, "Image/outline.ttf" +, 90, 0.18, 0, 0, $watermark_text);

      Thanks Rolf,

      I had never realised that I was mixing two different syntaxes...
      It is obvious now that you point it out and I am wondering how I could have not realised up to this point in time!

      That helps a lot 👍

Re: Indirect Object Syntax
by NERDVANA (Deacon) on Nov 07, 2021 at 09:07 UTC

    and because Perl's interpreter could potentially get this wrong, so could any human trying to understand the code.

    I think it is less about people understanding the intent of the author, and more about having newer Perl users get frustrated when a syntax that worked in one part of the code doesn't work in a different part of the code. Perl has several of these problems, and we can eliminate one of them by giving up on the indirect method syntax.

    An example of another ambiguity is when you want to generate multiple elements per iteration of "map".

    %set= map { $_ => foo($_) } @list; %set= map +( $_ => foo($_) ), @list;

    The first syntax is more idiomatic and looks better, but sometimes (depending on the code in the block and maybe also what comes after it) perl will decide that the block was a hashref notation and give you a nonsense error message. I've started always using the second notation just because it never parses incorrectly. This is *not* a universal recommendation, just a personal preference.

      > I've started always using the second notation just because it never parses incorrectly.

      I'd rather go for extra semicolon:

      • %set= map {; $_ => foo($_) } @list;
      demo with perl -de0
      DB<45> x map {a=>1} 1..3; # seeing hash, but co +mma missing syntax error at (eval 55)[c:/Strawberry/perl/lib/perl5db.pl:738] line +2, near "} 1" DB<46> x map {a=>1}, 1..3; # seeing hash again 0 HASH(0x2f501e8) 'a' => 1 1 HASH(0x2f4a1e8) 'a' => 1 2 HASH(0x2f4d0e0) 'a' => 1 DB<47> x map {;a=>1} 1..3; # seeing code 0 'a' 1 1 2 'a' 3 1 4 'a' 5 1 DB<48>

      YMMV! :)

      edit
      Anyway, Perl should check for the missing comma. I suppose this happens too late for the parser.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

        > Anyway, Perl should check for the missing comma. I suppose this happens too late for the parser.

        IIRC, the parser can only check the following token when deciding whether the { starts a block or an anonymous hash. That's also why the ; helps.

        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Indirect Object Syntax
by perlfan (Vicar) on Nov 08, 2021 at 23:37 UTC
    I've only ever seen it for the defacto construction of an "object". But using it like a keyword gives the impression that new is a keyword. Then you start giving people the impression that Perl is OOP. Then all of a a sudden you get an influx of JAVAtm programmers. THEN you get people actually trying to make Perl act like JAVAtm, and well... I don't think I need to say more. xD
        I don't understand what you're demonstrating there.