[Edinburgh-pm] a Perl surprise

Nick oinksocket at letterboxes.org
Wed Jul 18 10:31:14 PDT 2012


Hello,

On 18/07/12 14:36, Jeff Parsons wrote:
> I'd have expected the 8, yeah.
> 
> It's because there's no such thing as 'array context' in Perl, just scalar
> context and list context. Arrays can can be called in either context, but they
> themselves are not a context.

True, but I think you mentioned 'array context', not me... :)

> So really with the single scalar prototype it's just a list in scalar context,
> which as I'm sure you know will just store and discard each scalar to the left
> of a comma.
> 
> Notice that this is the same as your example:
> 
>  perl -le ' sub wtf($) { $_[0] }; @a = wtf (qw(6 7 8)); print @a;'
> 
> So what's really happening is qw(6 7 8) is getting assigned to $_[0] rather than
> @_, then the array, @a gets assigned the value of $_[0].
> 
> The fact that
> 
>  wtf qw(6 7 8)
> 
> is equivalent to:
> 
>   wtf (('6', '7', '8'))
> 

[...]

> That's actually incorrect and wouldn't be important anyway. '6' is 6 as a
> string, 6 is 6 as a number, they're not strictly the same thing. They're not
> stored the same internally and perl certainly won't just convert 6 to '6'
> unless you try to use 6 as a string.

To be honest, the quotes were an afterthought. The real point of that example is
the double brackets.    wtf(6, 7, 8) gets a compilation error.  wtf((6, 7, 8))
does not. And wtf qw(6 7 8) does not either, because of a diabolical combination
of Perl's grammar rules.

In agreement with General Wisdom, I also tend not to use prototypes, except
perhaps things like (&@).  However in this case I was attempting to use the ($)
prototype to try and force a function not to be "greedy", and instead consume
just one single argument.  The application is a configuration language - I'm
trying to warp Perl into a DSL.

   # (checks and balances elided)
   sub with($) { bless $_[0], 'With' }
   sub without($) { bless $_[0], 'Without' }
   sub combination { ... }

   # ...

   my $combination = combination (
      with [qw(toast eggs)],
      without ['gluten'],
      with [qw(sauce)],
   );

I could lose the square brackets and the prototypes and wrap everything in
brackets to coerce the functions to eat the right things:

  with (qw(toast eggs)), without('gluten'), with(qw(sauce))

But I wanted it to be robust so that if the brackets were forgotten, things
would fail safely rather than grouping in unintended ways.  (No, I'm not
convinced my alternative is entirely better, but... this is an experiment.)

Also in the name of robustness, I want to highlight potential accidental misuses
of 'with' and 'without', such like this:

  combine with qw(toast egg), without qw(gluten);  # Error: 'toast' not eaten

Bad syntax should fail instead of doing something surprising. Except to my
annoyance, this gave me something even more surprising as discussed:

  my $stuff = combine with qw(toast eggs), without qw(gluten);

Here, 'with' gets 'eggs', but 'toast' gets discarded on the floor, a somewhat
obscure warning about void context gets printed, and things charge onwards to
disappointment and/or disaster.  Not good.



> The simple answer as to why what happens happens is internally, by prototyping
> with a scalar you're just causing a
>
> $_[0] = qw(6 7 8) to happen.
>
>
> Hope I was clear in my explanation!

Thanks - I think I do understand what's happening retrospectively - it was a
mostly rhetorical post - but the design of the grammar perplexes me somewhat -
that and its ability to trip me up in cases like this (and I'm not new to Perl
by any means).

Cheers,

N

ps

> Regards,
> Geoff

You seem to be both Geoff and Jeff simultaneously?



More information about the Edinburgh-pm mailing list