6
#!/usr/bin/perl
use Modern::Perl;
while (<>)
{ chomp;
say reverse;  
}

The above code doesn't work but when I change 2nd last line to say scalar reverse; then it works fine. Why do I need to force it to be a scalar explicitly? Can't Perl DWIM?

Chankey Pathak
  • 21,187
  • 12
  • 85
  • 133
  • While testing this, there seems to be some inconsistency in when `reverse` implicitly uses `$_`, i.e. it does use `$_` in scalar context, but not in list context. (`@a = reverse` vs `$a = reverse`) Is this a bug, or working as intended? – TLP Oct 23 '11 at 14:37
  • Yes, I found the same bug (if it is). Have to define $_ explicitly. – Chankey Pathak Oct 23 '11 at 15:14
  • @TLP: http://stackoverflow.com/questions/7871120/reverse-doesnt-use-implicitly-in-list-context-is-this-a-bug – Chankey Pathak Oct 26 '11 at 02:56

2 Answers2

6

If I understand the documentation right, reverse normally operates on lists. In a list context used without arguments, it returns an empty list and by default doesn't assign it anywhere. In your example, say outputs the unchanged $_;

Forcing reverse into scalar context changes its behaviour and makes it reverse character strings, and use $_ by default. Because say can be used to print lists as well as scalars, it doesn't force its arguments into scalar context.

Perl probably does DWIM, just for given values of "I".

A breakdown of what reverse does when:

#!/usr/bin/env perl
use strict;
use v5.12;

my $inscalar = "foo bar";

# `reverse` is in list context, $in is coerced to a single-element list
my @outlist = reverse $inscalar;

# `reverse` is in scalar context, so it reverses strings
my $outscalar = reverse $inscalar;

say join(" | ", @outlist); # prints "foo bar"
say $outscalar; # prints "rab oof"

# the "normal" behaviour of `reverse`
my @inlist = qw(foo bar);
@outlist = reverse @inlist;
say join(" | ", @outlist); # prints "bar | foo"
cjm
  • 61,471
  • 9
  • 126
  • 175
millimoose
  • 39,073
  • 9
  • 82
  • 134
  • Assuming `$_ = "foo bar"`, then `@a = reverse` and `@a = reverse $_` should yield the same result. This, however, is not the case. – TLP Oct 23 '11 at 15:27
  • @TLP You're right. It seems like `reverse` without arguments in list context is just a no-op. – millimoose Oct 23 '11 at 15:42
3
Why do I need to force it to be a scalar explicitly?

As documented, reverse reverses the order of the elements of the argument list when used in list context. For example, reverse 'a', 'b', 'c' returns 'c', 'b', 'a'.

Can't Perl DWIM?

Yes, it could do what you want (scalar behaviour when no argument expression is present).

It could be confusing, though. I can't think of any other operator that changes behaviour based on a lack of an argument expression (as opposed to just lack of arguments).

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • There is something strange here. I am not getting the same result for `@array = reverse` and `@array = reverse $_`, but I do get the intended result with `$var = reverse`. So, `reverse` uses `$_` implicitly, *except* in list context? – TLP Oct 23 '11 at 14:13
  • Yes. A bare `reverse` is only treated as `reverse $_` in scalar context. This is in `perldoc -f reverse`: "Used without arguments *in scalar context*, `reverse()` reverses `$_`." – socket puppet Oct 23 '11 at 18:46
  • 1
    @socketpuppet You're right, it is documented. Of sorts. Feels unfamiliar to have a function that does *not* use `$_` by default with missing arguments, but instead uses... nothing? And does not even give a warning about it. Feels like a bug to me. – TLP Oct 23 '11 at 21:52
  • Thanks @socket puppet, Removed false claim that `say reverse;` means `say reverse $_;` – ikegami Oct 24 '11 at 20:33