Mr. Conway suggests the very firm, “Never modify $_ in a list function.” I’m torn on this.
I’m torn because this seems to discard a useful part of Perl; the list functions for, map, grep, and first all set $_ to an alias of the item in the list. Mr. Conway suggests that any time you change that, you’re making an error.
I can’t argue that it’s often an error. But should we say, “Don’t take advantage of this useful tool because other people might not understand the language.” I’m torn. This is a function of core Perl and it’s been this way forever. There might be plenty of interesting uses for it.
I would say, “Don’t use this casually.” and suggest that other ways be considered first. If it’s really needed, document why and be prepared to defend yourself in code review. (You do code reviews, right?)
$_ is only an alias because of performance. Someone should never modify $_ in map, grep because it destroys the whole meaning of map, grep. map, grep are functions from the functional world with immutability in mind. That means creating something new from something existing, without modifying it.
Just because something is possible doesn’t mean you should do it, and modifying $_ in a map, grep is one of those things.
You’ve done plenty of just that yourself in this series, you know. So, should we?
Nope.
By that token you would also have to always return one thing from a map block because that’s how it works in functional languages.
That whole line of argument is nonsense. Perl’s map is Perl’s map, not Lisp’s, and Perl is Perl, not Lisp.
> By that token you would also have to always return one thing from a map block because that’s how it works in functional languages.
That Perl doesn’t force a return value on every map iteration absolutly doesn’t change anything what i said.
> Perl’s map is Perl’s map, not Lisp’s, and Perl is Perl, not Lisp.
I never talked about Lisp.
I believe in saying what you mean, and I believe that side-effects in map are pretty confusing and that there’s usually a clearer way to do it — e.g. “$_++ for @things” rather than “map { $_++ } @things”. If I was really using map for its return value *and* for some side-effect on the original list, I might let it stand, but I would be pretty tempted to add a comment to the effect that it modifies its input, because readers don’t and shouldn’t expect map to do that.
“the list functions for, map, grep, and first”
Except ‘for’ is a statement modifier, rather than a list function. It’s fine to do things like this, IMHO:
++$_ for @things;
$_ .= ‘@default.domain’ for grep !/\@/, @emails;
but I wouldn’t write any of these:
map { ++$_ } @things;
grep { s/\@.*$// } @emails;
You don’t typically need to modify things inside the map {…} block, because that’s a job for the ‘for’ statement modifier. I think you may just have a slight misunderstanding of the actual guideline? Seems it’s more likely that the advice applies to the BLOCK in map BLOCK LIST or grep BLOCK LIST syntax (it’s not really a function in those, although some map-like constructs do take a coderef).
… also I think I’m probably just repeating what Andrew already said, that’ll teach me to reload the page before writing comments!
The /r regex flag is so useful here.