Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

RFC: Context tutorial

by kyle (Abbot)
on Jan 12, 2009 at 17:33 UTC ( [id://735750]=perlmeditation: print w/replies, xml ) Need Help??

This is a draft tutorial on the sticky subject of context. It's terrible and in dire need of your comments. Please, if you value the quality of the tutorials at this fine site, read this with a patch over the uncritical eye and tell me where it's unclear, awkward, and just plain wrong so I can fix it before putting it in the Tutorials section. Don't let it go to those hallowed halls in this sad pathetic state.

I wrote it by reading the Camel book's three pages about context, reducing them to nine bullet points, and then expanding the bullet points into this, once I'd forgotten what I read. Really, it needs your help.

Thank you.

"You will be miserable until you learn the difference between scalar and list context"

Programming Perl 3rd ed, page 69

An introduction to context

Context is everywhere in Perl. Every operand is evaluated in some context. Every operator imposes a context to its operands. Every expression is constrained by the context in which it is evaluated.

The two main contexts are scalar context and list context. Another is void. An operator that works on scalars imposes a scalar context. You can tell which kind of context an operator has by checking the standard Perl documentation. Every operator that creates a list context has "LIST" in its syntactic description.

Context matters because operands in Perl can behave differently according to context. This is sort of like overloading. A function (or other expression) can provide different results depending on the context it's in.

Context in action

For these examples, I'll use localtime since it's a common function which evaluates differently according to context. In a list context, it returns a series of numeric values that describe the current time. In a scalar context, it returns a human readable string to describe the current time.

# Example 1. # @now will be (40, 51, 20, 9, 0, 109, 5, 8, 0) @now = localtime(); # Example 2. # $now will be "Fri Jan 9 20:51:40 2009" $now = localtime();

In example 1, we assign localtime to an array, which imposes a list context. The array takes in all the values returned by localtime.

In example 2, localtime is assigned to a scalar, which imposes a scalar context. In that context, localtime returns the string, "Fri Jan 9 20:51:40 2009".

# Example 3. # $sec will be 40, $min will be 51, $hr will be 20 ($sec,$min,$hr) = localtime();

In example 3, localtime is assigned to a list expression which has three scalar variables in it (wrapped in parentheses). Because of the list expression, localtime() is in list context. The three variables get the first three values from the list that localtime() returns, and its other return values are discarded.

Note that this assignment is also to a list expression:

my ($x) = localtime();

To write it without the list context, drop the parentheses.

my $x = localtime();

Context propagates into sub calls

When control reaches a return, the operand to the right of that return finds itself in the context that the sub was in when it was called. That means that in the following code, localtime is in the list context from print.

print clock(); sub clock { return localtime(); }

A sub's calling context might come from some operator high up in the call stack.

Forcing scalar context

If you want to force a scalar context on an operand, use scalar. For example:

print scalar localtime();

When you print, its operands are in list context. It accepts a list of values to be printed. If you just "print localtime()", it will print something like "40512090109580" (the list of values all stuck together), but "scalar localtime()" looks like "Fri Jan 9 20:51:40 2009".

Forcing list context

To force a list context where there wouldn't be one otherwise, assign to an empty list.

my $match_count = ()= /x/g;

The "/x/g" in scalar context would match only once, but with the list context, it finds every match. The value of "()=" in scalar context is the number of items in the list.

Void context

Void context is a special case of scalar context. It's the context of something that doesn't have an operator working on it. The value of a thing in void context is discarded, not used for anything. For example, the body of a while loop is in void context.

while (<>) { ponder( $_ ); # void context }

The only way void context is like scalar context is that it's not list context. The only way void context is not like scalar context is that some things in void context can generate warnings that they wouldn't generate in a scalar context.

Determining context with wantarray

A sub can determine what context it was called in by using wantarray. If the sub is in list context, wantarray will return a true value. In scalar context, it returns a false value that's defined. In void context, it will return undef.

sub print_context { if ( wantarray() ) { print "list\n"; } elsif ( defined wantarray() ) { print "scalar\n"; } else { print "void\n"; } } print_context(); ()= print_context(); scalar print_context(); __END__ void list scalar

More flavors of scalars

The Camel book subdivides scalar context into numeric, string, and don't care context, but it's more useful to think of these as casts.

Scalar assignment is a "don't care" because numbers and strings are treated the same—passed through to the scalar variable to have and to hold just as they are. Another "don't care" cast is boolean because strings and numbers aren't converted when being interpreted as true or false. To learn more about how values are treated in boolean context, see True or False? A Quick Reference Guide

In numeric and string contexts, a variable might undergo a transformation from one to the other in order to facilitate the workings of the operator. In the following example, a string is cast to a number to accommodate the numeric operation of the postfix "--" operator.

my $beer_inventory = '99 bottles'; print "how much beer? $beer_inventory\n"; $beer_inventory--; # take one down... print "how much beer? $beer_inventory\n"; __END__ how much beer? 99 bottles how much beer? 98

To force an expression to a string, use "''." (that is, append to an empty string). To interpret an expression as a number, use "0+" (that is, add zero). It's better to use "0+" than some other identity function (such as "1*") because that's what overload uses to designate the numeric cast. To force a boolean interpretation, use "!!" (that is, boolean negation twice).

my $s = "12 monkeys"; my $n = 31337; my $stringified = ''. $n; # "31337" my $numified = 0+ $s; # 12 my $bool = !! $n; # 1

Every cast is possible. Strings can be cast to numbers or booleans. Numbers can be cast to strings or booleans. Booleans can be cast to numbers or strings.

Context clash

A single value (such as a scalar) in a list context becomes a list with one item.

my $scaley = 'snake'; my @listy = $scaley; # does the same thing: #my @listy = ('snake');

That's simple enough. What about the other way around? In scalar context, a list of expressions separated by commas evaluates to the whatever the last item evaluates to in scalar context.

my @t = ( 'one', 'two' ); my $x = ( 'one', 'two' ); # 'two' my $y = ( 'larry', 'moe', @t ); # 2 my $z = ( 'old', 'man', localtime() ); # "Fri Jan 9 20:51:40 2009"

Note that an array or hash slice counts as a "list" for this behavior. For a description of slices, see Slices.

Other things ordinarily used in list context have their own special scalar context behaviors. For a list of these, see On Scalar Context.

Interpolative context

This isn't a full fledged context like scalar and list context, but it helps to know in which places variables are expanded (interpolated) into their values. For a full list of these, see Quote and Quote-like Operators. For a brief example, see below.

my $friend = 'Perl'; my $literal = '$friend'; # literally, '$friend' my $expanded = "$friend"; # literally, 'Perl'

To reiterate, this isn't a context the way "scalar context" is a context, and it isn't what we normally mean when we say simply "context." Still, it may help to think of it as a context when one is considering interpolation. For the full scoop including many other places this is relevant, see the full documentation (Quote and Quote-like Operators).

More context

If the deep dark magic of wantarray leaves you wanting for more, look into Want and Contextual::Return, which are advertised to detect contexts I have not mentioned here. Also, look at overload for creating an object which can interpret itself differently in numeric and string contexts.

Another good source of information about context is What is Scalar Context? by Dominus.

Thanks to belg4mit, tye, theorbtwo, Arunbear, bart, zentara, ikegami, Limbic~Region, Narveson, jdporter, and moritz for commenting on an earlier draft!

Thank you for reading this far! Having endured so much, please take a short time longer and tell me which parts of it are the most horrendous. Thanks again.

Update: I've made small changes based on numerous private comments I've received. (Y'all are welcome to point out my mistakes publicly. I won't be offended.)

Replies are listed 'Best First'.
Re: RFC: Context tutorial
by Porculus (Hermit) on Jan 12, 2009 at 23:41 UTC

    I was expecting more to be made of where contexts appear: particularly the fact that subroutine arguments are normally list context (you only mention this explicitly for print; the point could perhaps be generalised), and also -- without going into all the messy details of prototypes -- the fact that they sometimes unexpectedly aren't.

    (I'm feeling particularly sensitive to that one because I was bitten by it the other day: took me a while to work out why

    my @loc = ($offset, $length); my $part = substr $string, @loc;
    was always just stripping the first two characters from my string...)

    On re-reading, I see you may be touching on this in your preamble, but there you refer only to "operators", which I initially took as excluding functions like substr(). So maybe all that's needed is a little clarification up top.

      I don't want to get into detailing what context is imposed by every Perl built-in, but I think you're right that I should mention that user-defined sub arguments are in list context. I'll put that in before I post it to Tutorials.

Re: RFC: Context tutorial
by ww (Archbishop) on Jan 13, 2009 at 11:06 UTC

    Since you're going to be helping newcomers with this tut, you might want to provide some brief explanations of how they can try out your illustrations.

    IOW, you might want to occasionally go off-topic. For instance, just before the second code block ("Example 3") something like this might be valuable:

    Go ahead; try it out. If you're on a Windows box, you can run the code in Example 1 from the command prompt like this:

    perl -e "@now = localtime(); for $now(@now) {print $now;}" 432351301092120 perl -e "$now = localtime(); print $now;" Tue Jan 13 05:26:30 2009

    If you're on Linux or a derivative like newer Macs, replace the double quotes above with single quotes.

    Now, back to context.

    Downside: You'll need to add a bit to some of the more fragmentary code blocks -- for instance, for example 6 *1, you might add something like this:

    To see this effect at the command prompt, try:

    perl -e "my $x = clock(); sub clock { return localtime(); } print $x;" Tue Jan 13 05:47:55 2009

    For contrast:

    perl -e "my @x = clock(); sub clock { return localtime(); } print @x;" 404851301092120

    As you can see, the way your assign the value returned by the sub forces a particular context.

    *1 Yes, number them all, not merely for consistency, but also because doing so would make it easier to refer back to a prior example, when elaborating a distinction seems appropriate.

    htih
Re: RFC: Context tutorial
by jvector (Friar) on Jan 13, 2009 at 22:40 UTC
    I don't have "editorial" comments to offer, but as one who has "context" on the (rapidly growing) list of "things about Perl to learn more about" (and therefore, I assume, one of your target audience) I'd like to say that this hits the spot and occurs as exactly the right sort of level. Thank you!

    This signature was issued as a beta trial at Christmas
Re: RFC: Context tutorial
by gone2015 (Deacon) on Jan 14, 2009 at 01:30 UTC

    A trap I have fallen into is forgetting that items in a list are themselves evaluated in list context.

    This seems obvious when @a = (1, @b, @c), but not so obvious when print 1, reverse('olleh') or similarly in a subroutine call. (You mention print, but I think the problem is more general.) Nor so obvious in $a = { pkg => caller, ... }... Nor foo(1, m/q/) when the match fails.

    You talk about operators affecting numbers and strings. Operators can also override context, eg @a+0 returns the number of elements in @a in any context. And cunning things like $year = (localtime)[5] + 1900 will evaluate localtime in list context.

    The other edge case I've encountered is @a = 12 x 10, which doesn't yield an array of 10 elements, list context notwithstanding.

Re: RFC: Context tutorial
by gwadej (Chaplain) on Jan 13, 2009 at 20:39 UTC

    In the section Context Clash, you show some interesting effects of assigning a list of expressions separated by commas to a scalar. I was surprised at the results of the third example. (Intuition based on subroutine calls is incorrect because subroutine arguments are in list context.)

    However, your statement

    Note that an array or hash slice counts as a "list" for this behavior.

    does not appear to agree with the perl debugger. I got the following from a quick test:

    my @t = qw/one two/; my $y = ( 'larry', 'moe', @t ); print "$y\n"; # prints 2 $y = ( 'larry', 'moe', @t[0,1] ); print "$y\n"; # prints two
    G. Wade

      Thanks for your comment. I guess I need to clarify that. The code you show does just what I would expect and also what I thought I described.

      The array slice, @t[0,1], would be a list but for the scalar context. Because of the context, it evaluates to the last item in that list (i.e., $t[1]). This kind of behavior was the subject of Why does assignment change the result?, if you want to read about it more.

      Before I post to Tutorials, I'll probably add your example to the others in that section to make this more obvious and probably also reword the line you quoted.

      Thanks again for pointing this out.

        I understand what it did. What I feel you were unclear about is the difference between:

        my $y = ( 'moe', 'larry', @t );

        and

        my $y = ( 'moe', 'larry', @t[0,1] );

        They react differently, and it takes a little thought to see why.

        G. Wade

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://735750]
Approved by Corion
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (3)
As of 2024-04-14 03:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found