In Essential Skills for Perl 5 Programmers I mentioned that no one can be an adept Perl programmer without understanding context. This trips up many, many people -- and you often hear (unfair) criticisms of Perl 5 based on misunderstandings and guesses about how context works.
Context is reasonably easy to explain. (The previous sentence is grammatically correct.) Contexts is not difficult to understands. (The previous sentence is grammatically incorrect, even if you speak the Queen's English.)
If you can find the errors in the previous paragraph, you can understand quantity context in Perl 5: like subject-verb agreement in terms of number, expressions in Perl 5 can behave differently in contexts that imply zero, one, or more results.
fetch_something_awesome(); # void context
my $item = fetch_something_awesome(); # scalar context
my @items = fetch_something_awesome(); # list context
Context gets a little bit trickier when you need to coerce what would normally be one context into another:
my ($item) = fetch_something_awesome(); # list context
push @items, scalar fetch_something_awesome(); # scalar context
If you know the visual cues (if you don't randomly sprinkle punctuation about your program until it works), those are easy to understand as well.
The subtlety comes when dealing with complex contexts, usually with nested expressions:
# list context, thanks to say
say reverse $name;
my %values =
(
# list context, thanks to hash assignment
name => get_name(),
rank => get_rank(),
);
# list context (param flattening)
$screen->flip( $fleet->get_spaceships() );
This is often where more fair criticisms of Perl 5 suggest that context may not be worth it, because you have to understand what a line of code means and what it implies to read it correctly.
There's a fair point there, but it's also silly in some ways. Skimming code which calls other functions may give you some idea of what those functions do, but you rely only on the names of those functions and not their documentation to tell you any other details. Do they modify global or thread-local variables? Do they have caching or performance characteristics? Do they block? Do they require special initialization or error handling? Do they return special values?
The valid point is that chaining multiple expressions into complex compound expressions can have interesting effects. (I see this in Haskell code often; invisible partial application means that I personally can't skim Haskell code without tracking down the arity of functions to figure out what happens where.)
That's no argument against language features. It's an argument
against making expressions more complex than necessary. Note that the same
argument applies against complex prefix-unless
expressions.
unless
can be amazingly useful when used properly. If you abuse
it, you make amazing problems. Don't make problems.
In most cases, I intend to avoid to use 'context', simply because it makes me feel that I would make mistakes. Although in most cases, I don't.
A comment on Reddit about pre-++ vs post-++ reminded me of this page. Specifically: If anybody says C doesn't have context, remind them of ++ and *, which do different things in different places. And of course there's always operator overloading in C++.
On the other hand, I don't know if anybody *actually* says "C doesn't have context and Perl does, ergo C is better", so it's probably just a straw man. :) But I thought I'd mention it, nevertheless.