Style question: map versus foreach

  • Thread starter LaDainian Tomlinson
  • Start date
B

Ben Morrow

Darin McBride said:
sub foo()
{
return qw(this is a small array);
}

foo();

And you're saying something in void context doesn't return anything?

my $x = foo();

And you're saying that this returns a list?
However, given that the difference between map and
foreach was solely that return difference, I don't see a point behind
the patch.

I don't think that is 'given', at least not by Abigail. Hence the
argument...
Perhaps if you work exclusively with perl 5.8+, that might be true.
However, this doesn't work for two points:
1a) Some of us still work in shops that haven't upgraded from 5.6 yet
(no matter how hard I've pushed)
1b) map still allocates that array *conceptually* in the reader's
mind, even if no allocation actually takes place on the physical
machine.

Err, no... at least, not in my mind; any more than

my $x = 5;

conceptually allocates a scalar return value. As pointed out above, a
function called in void context *never* conceptually allocates a
return value. The fact that map in fact did so was a bug.
I respectfully must disagree with your definition of "side effects".

About the only objective definition of 'side-effect' is 'any effect a
function has other than its return value'. Yes, this is Lisp-ish. No,
that does not make it wrong.

Ben
 
S

Steve Grazzini

Ben Morrow said:
my $x = foo();

And you're saying that this returns a list?

I can't answer for Darin, but I think it does return a list.

% perl -Dts -le 'sub f { "one", "two", "three" } f()'

EXECUTING...

=>
(-e:0) enter
=>
(-e:0) nextstate
=>
(-e:1) pushmark
=> *
(-e:1) gv(main::f)
=> * GV()
(-e:1) entersub
=>
(-e:1) nextstate
=>
(-e:1) pushmark
=> *
(-e:1) const(PV("one"\0))
=> * PV("one"\0)
(-e:1) const(PV("two"\0))
=> * PV("one"\0) PV("two"\0)
(-e:1) const(PV("three"\0))
=> * PV("one"\0) PV("two"\0) PV("three"\0)
(-e:1) list
=> PV("three"\0)
(-e:1) leavesub
=> PV("three"\0)
(-e:1) leave

This is void context, of course, and commas instead of qw(), but it
works just like your example. The subroutine returns a list and a
list in void or scalar context evaluates [pp.c: pp_list] to its last
element.

I suppose it would also be possible to say that the subroutine returns
the *result* of evaluating a list in void context. And likewise, that
this one returns the result of evaluating an array in scalar context:

% perl -Ds -le 'sub f { @INC } scalar f()'

But I'd rather say that this returns an array.

The more common explanation -- that the comma operator in scalar
context works "like it does in C", i.e. evaluating and discarding its
LHS and returning its RHS -- also fits the result, but it's not quite
as accurate.
 
R

Roy Johnson

Ben Morrow said:
It also gives a specific use for map in void context: to call a
function in list context but discard the result.

Which is another thing that smacks of bad practice.
 
B

Ben Morrow

Steve Grazzini said:
I can't answer for Darin, but I think it does return a list.

% perl -Dts -le 'sub f { "one", "two", "three" } f()'
This is void context, of course, and commas instead of qw(), but it
works just like your example. The subroutine returns a list and a
list in void or scalar context evaluates [pp.c: pp_list] to its last
element.

I'm afraid I can't really follow that: which line is supposed to be
'the return value'?; but it makes no difference anyway. We are talking
about the conceptual return value, ie. about what Perl returns; if
perl in fact builds a list only to discard all but the last element
this is a bug, like the one in map was.
I suppose it would also be possible to say that the subroutine returns
the *result* of evaluating a list in void context. And likewise, that
this one returns the result of evaluating an array in scalar context:

% perl -Ds -le 'sub f { @INC } scalar f()'

But I'd rather say that this returns an array.

But that would be quite wrong. In Perl, a function *cannot* return an
array, only a list if in list context or a scalar if in scalar. For
instance, what about this

% perl -le'sub f { caller } print scalar f'

? Would you say this returns 'the caller function', which is then
evaluated in scalar context? It certainly doesn't return a list:
according to you, the result of 'evaluating a list in scalar context'
is its last element, which is not what is returned.

In fact, as Randal keeps saying, there is *no such thing* as 'a list
in scalar context'.
The more common explanation -- that the comma operator in scalar
context works "like it does in C", i.e. evaluating and discarding its
LHS and returning its RHS -- also fits the result, but it's not quite
as accurate.

OK, the comma operator in scalar context builds a list and then
discards all but the last element. Just out of interest (my perl isn't
built with DEBUGGING), what does this do

% perl -Dts -le'scalar (1, 2, 3)'

? Is the whole list built, or are the values discarded as it goes
along?

Ben
 
S

Steve Grazzini

Ben Morrow said:
But that would be quite wrong. In Perl, a function *cannot* return
an array, only a list if in list context or a scalar if in scalar.

That's true, of course. I think I got it right earlier: f() returns
the result of evaluating @INC in scalar context when it's called in
scalar context, and the result of evaluating @INC in list context when
it's called in list context. Condensing this to "f() returns @INC" is
less precise, but maybe it's close enough to be useful, assuming that
we all know you can't *really* return an array from a subroutine and
do "push foo(), $elt" or whatever.
For instance, what about this

% perl -le'sub f { caller } print scalar f'

? Would you say this returns 'the caller function', which is then
evaluated in scalar context?

Well, no. That would be silly. :)
It certainly doesn't return a list:

It certainly doesn't!
according to you, the result of 'evaluating a list in scalar context'
is its last element, which is not what is returned.

That's because (as you must know) caller() doesn't return a list in
scalar context. Since there's no list, this doesn't have anything
to do with the behavior of a list in scalar context.
In fact, as Randal keeps saying, there is *no such thing* as 'a list
in scalar context'.

That's a simplification, too. It's probably the best thing to tell
people who are trying to figure out what a built-in does in scalar
context based on what it does in list context. But it's not the whole
story -- perldata explains this more accurately:

If you evaluate an array in scalar context, it returns the length
of the array. (Note that this is not true of lists, which return
the last value, like the C comma operator, nor of built-in functions,
which return whatever they feel like returning.)
OK, the comma operator in scalar context builds a list and then
discards all but the last element. Just out of interest (my perl
isn't built with DEBUGGING), what does this do

% perl -Dts -le'scalar (1, 2, 3)'

? Is the whole list built, or are the values discarded as it goes
along?

Neither. :)

Here the compiler knows that the potential list is in scalar context,
and it puts the "1" and "2" in void context, since they're just going
to be discarded. Constants in void context can be optimized away
(i.e. discarded at compile-time) and that's what happens here.

This is the zen-like "There Is No List..." scenario from the FAQ:

As a side note, there's no such thing as a list in scalar con-
text. When you say

$scalar = (2, 5, 7, 9);

you're using the comma operator in scalar context, so it uses
the scalar comma operator. There never was a list there at
all! This causes the last value to be returned: 9.

And perlfunc:

A named array in scalar context is quite different from what would
at first glance appear to be a list in scalar context. You can't
get a list like "(1,2,3)" into being in scalar context, because the
compiler knows the context at compile time. It would generate the
scalar comma operator there, not the list construction version of
the comma. That means it was never a list to start with.

And they're correct, more or less, for the examples they give. But the
bit about the "scalar comma operator" seems to be misguided. If any of
the earlier elements of those lists were variables, a list really would
be created at runtime.

% perl -Dts -le 'scalar($$, 42, 43)'

EXECUTING...

=>
(-e:0) enter
=>
(-e:0) nextstate
=>
(-e:1) pushmark
=> *

Here's the list (note the missing "42").

(-e:1) gvsv(main::$)
=> * IV(14592)
(-e:1) const(IV(43))
=> * IV(14592) IV(43)

Here's the op that evaluates the list in scalar context.

(-e:1) list

And here's the result.

=> IV(43)
(-e:1) leave
 
B

Ben Morrow

Steve Grazzini said:
That's true, of course. I think I got it right earlier: f() returns
the result of evaluating @INC in scalar context when it's called in
scalar context, and the result of evaluating @INC in list context when
it's called in list context. Condensing this to "f() returns @INC" is
less precise, but maybe it's close enough to be useful, assuming that
we all know you can't *really* return an array from a subroutine and
do "push foo(), $elt" or whatever.
OK.


Well, no. That would be silly. :)

Well.... I don't quite see as it's sillier than saying the sub above
returns @INC. This returns the result of evaluating caller() in list
context when in list context, and the result of evaluating caller() in
scalar context when in scalar context.

Both are simply a constructive use of sloppy language.
That's because (as you must know) caller() doesn't return a list in
scalar context. Since there's no list, this doesn't have anything
to do with the behavior of a list in scalar context.

But my point was that neither was there a list in the @INC
case. '@INC' doesn't return a list in scalar context either: it
returns the number of elements it contains.

And, to get back to the original argument :), neither does map()
return a list in void context. It returns nothing.

But the bit about the "scalar comma operator" seems to be misguided.
If any of the earlier elements of those lists were variables, a list
really would be created at runtime.

% perl -Dts -le 'scalar($$, 42, 43)'

EXECUTING...

=>
(-e:0) enter
=>
(-e:0) nextstate
=>
(-e:1) pushmark
=> *

Here's the list (note the missing "42").

(-e:1) gvsv(main::$)
=> * IV(14592)
(-e:1) const(IV(43))
=> * IV(14592) IV(43)

Here's the op that evaluates the list in scalar context.

(-e:1) list

And here's the result.

=> IV(43)
(-e:1) leave

Interesting.....

So a better statement of all this is perhaps:

There is no 'scalar comma operator'. The comma operator builds a
list. A list evaluated in scalar context evaluates each of its members
in void context; except the last which is evaluated in scalar context
and returned. This may be optimized away at compile time (as usual).

Hmmm. This applies at complie time, not at run time. In

sub c {...}

my $x = (c, c);

the elements of the list are 'call sub c', rather than the results of
those calls; rather like returning 'the caller function' above :).
Very functional, and not at all suitable as a way of explaining it to
people :).

Ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,797
Messages
2,569,646
Members
45,374
Latest member
VernitaBer

Latest Threads

Top