diff --git a/sections/chapter_05.pod b/sections/chapter_05.pod index f2f1aebb..d784dec1 100644 --- a/sections/chapter_05.pod +++ b/sections/chapter_05.pod @@ -4,13 +4,13 @@ X X A I (or I) in Perl is a discrete, encapsulated unit of -behavior. It may or may not have a name. It may or may not consume incoming -information. It may or may not produce outgoing information. It represents a -type of control flow, where the execution of the program proceeds to another -point in the source code. +behavior. A program is a collection of little black boxes where the interaction +of these functions governs the control flow of the program. A function may have +a name. It may consume incoming information. It may produce outgoing +information. Functions are a prime mechanism for abstraction, encapsulation, and re-use in -Perl 5; many other mechanisms build on the idea of the function. +Perl 5. L diff --git a/sections/functions.pod b/sections/functions.pod index 2a9a72e7..c62c4d92 100644 --- a/sections/functions.pod +++ b/sections/functions.pod @@ -13,14 +13,13 @@ Use the C builtin to declare a function: =end programlisting -Now C is available for invocation anywhere else within the program, -provided that the symbol--the function's name--is visible. +Now C is available for invocation anywhere else within the program. X -You do not have to I a function at the point you declare it. You may -use a I to tell Perl that you intend for the function to -exist, then delay its definition: +You do not have to I a function at the point you declare it. A +I tells Perl to remember the function name even though you +will define it later: =begin programlisting @@ -28,11 +27,10 @@ exist, then delay its definition: =end programlisting - =begin sidebar -You do not have to declare Perl 5 functions before you use them, except in the -special case where they modify I the parser parses them (L). +Forward declarations are only useful in the two rare cases of attributes +(L) and autoloading (L). =end sidebar @@ -40,7 +38,8 @@ special case where they modify I the parser parses them (L). X -To invoke a function, mention its name and pass an optional list of arguments: +Use postfix (L) parentheses and a function's name to invoke that +function and pass an optional list of arguments: =begin programlisting @@ -52,13 +51,12 @@ To invoke a function, mention its name and pass an optional list of arguments: =begin sidebar -You can I omit parameter-grouping parentheses if your program runs -correctly with the C pragma enabled, but they provide clarity to the -parser and, more importantly, human readers. +These parentheses are not strictly necessary even with C enabled, but +they provide clarity to human readers and Perl's parser. =end sidebar -You can, of course, pass multiple I of arguments to a function: +Function arguments can be arbitrary expressions, including simple variables: =begin programlisting @@ -79,10 +77,9 @@ X X> X -Inside the function, all parameters exist in a single array, C<@_>. If C<$_> -corresponds to the English word I, C<@_> corresponds to the word I. -Perl I all incoming parameters into a single list. The function -itself either must unpack all parameters into any variables it wishes to use or +A function receives its parameters in a single array, C<@_> +(L). Perl I all incoming parameters into a +single list. The function must either unpack all parameters into variables or operate on C<@_> directly: =begin programlisting @@ -100,8 +97,7 @@ operate on C<@_> directly: =end programlisting -C<@_> behaves as does any other array in Perl. You may refer to individual -elements by index: +C<@_> behaves as a normal Perl array. Refer to individual elements by index: =begin programlisting @@ -116,10 +112,8 @@ elements by index: =end programlisting -You may also C, C, C, C, C, and slice C<@_>. -Inside a function, the C and C operators operate on C<@_> -implicitly in the same way that they operate on C<@ARGV> outside of any -function: +... or C, C, C, C, C, and slice C<@_>. Most +code uses C or list unpacking: =begin programlisting @@ -129,19 +123,25 @@ function: say "Hello, $name!"; } + sub greet_two_shift + { + my ($hero, $sidekick) = @_; + say "Well if it isn't $hero and $sidekick. Welcome!"; + } + =end programlisting =begin sidebar -While writing C may seem clearer initially, taking advantage of the -implicit operand to C is idiomatic in Perl 5. +Remember that the array builtins default to C<@_> as operand within functions. +Take advantage of this idiom. =end sidebar -Take care that assigning a scalar parameter from C<@_> requires C, -indexed access to C<@_>, or lvalue list context parentheses. Otherwise, Perl 5 -will happily evaluate C<@_> in scalar context for you and assign the number of -parameters passed: +Assigning a scalar parameter from C<@_> requires C, indexed access to +C<@_>, or lvalue list context parentheses. Otherwise, Perl 5 will happily +evaluate C<@_> in scalar context for you and assign the number of parameters +passed: =begin programlisting @@ -154,7 +154,7 @@ parameters passed: =end programlisting List assignment of multiple parameters is often clearer than multiple lines of -C. Compare: +C. Compare: =begin programlisting @@ -196,33 +196,29 @@ rest to another function: =end programlisting -The dominant practice seems to be to use C only when your function must -access a single parameter and list assignment when accessing multiple -parameters. +Use C when your function needs only a single parameter. Use list +assignment when accessing multiple parameters. =begin sidebar -See the C, C, and C -modules on the CPAN for declarative parameter handling. +X> +X> +X> +X> + +The C, C, C, and +C CPAN modules offer more powerful parameter +handling. =end sidebar =head2 Flattening -The flattening of parameters into C<@_> happens on the caller side. Passing a -hash as an argument produces a list of key/value pairs: +Parameter flattening into C<@_> happens on the caller side of a function call. +Passing a hash as an argument produces a list of key/value pairs: =begin programlisting - sub show_pets - { - my %pets = @_; - while (my ($name, $type) = each %pets) - { - say "$name is a $type"; - } - } - my %pet_names_and_types = ( Lucky => 'dog', Rodney => 'dog', @@ -232,20 +228,26 @@ hash as an argument produces a list of key/value pairs: show_pets( %pet_names_and_types ); + sub show_pets + { + my %pets = @_; + while (my ($name, $type) = each %pets) + { + say "$name is a $type"; + } + } + =end programlisting -The C function works because the C<%pet_names_and_types> hash -flattens into a list. The order of the pairs within that flattened list will -vary, but pairs will always appear in that list with the key first immediately -followed by the value. The hash assignment inside the function C -works essentially as the more explicit assignment to C<%pet_names_and_types> -does. +When Perl flattens C<%pet_names_and_types> into a list, the order of the +key/value pairs from the hash will vary, but the list will always contain a key +immediately followed by its value. Hash assignment inside C works +essentially as the more explicit assignment to C<%pet_names_and_types> does. -This is often useful, but you must be clear about your intentions if you pass -some arguments as scalars and others as flattened lists. If you wish to make a -C function, where one parameter is the type of pet to -display, you must pass that type as the I parameter (or use C to -remove it from the end of C<@_>): +This flattening is often useful, but beware of mixing scalars with flattened +aggregates in parameter lists. To write C function, where +one parameter is the type of pet to display, pass that type as the I +parameter (or use C to remove it from the end of C<@_>): =begin programlisting @@ -278,11 +280,10 @@ remove it from the end of C<@_>): Z X -As with any lvalue assignment to an aggregate, assigning to C<%pets> within the -function I all of the remaining values from C<@_>. If the C<$type> -parameter came at the end of C<@_>, Perl would attempt to assign an odd number -of elements to the hash and would produce a warning. You I work around -that: +List assignment with an aggregate is always greedy, so assigning to C<%pets> +I all of the remaining values from C<@_>. If the C<$type> parameter +came at the end of C<@_>, Perl would warn about assigning an odd number of +elements to the hash. You I work around that: =begin programlisting @@ -296,18 +297,17 @@ that: =end programlisting -... at the expense of some clarity. The same principle applies when assigning -to an array as a parameter, of course. Use references (L) to avoid -flattening and slurping when passing aggregate parameters. +... at the expense of clarity. The same principle applies when assigning to an +array as a parameter, of course. Use references (L) to avoid +unwanted aggregate flattening and slurping. =head2 Aliasing X X -One useful feature of C<@_> can surprise the unwary: it contains aliases to the -passed-in parameters, until you unpack C<@_> into its own variables. This -behavior is easiest to demonstrate with an example: +C<@_> contains a subtlety; it I function arguments such that you can +modify them directly. For example: =begin programlisting @@ -324,15 +324,15 @@ behavior is easiest to demonstrate with an example: =end programlisting -If you modify an element of C<@_> directly, you will modify the original -parameter directly. Be cautious. +Modify an element of C<@_> directly and you will modify the original parameter. +Be cautious, and unpack C<@_> rigorously. =head1 Functions and Namespaces -Every function lives in a namespace. Functions in an undeclared -namespace--that is, functions not declared after an explicit C -statement--live in the C
namespace. You may specify a function's -namespace outside of the current package at the point of declaration: +Every function has a containing namespace (L). Functions in an +undeclared namespace--functions not declared after an explicit C +statement--are in the C
namespace. You may also declare a function within +another namespace by prefixing its name: =begin programlisting @@ -342,19 +342,13 @@ namespace outside of the current package at the point of declaration: =end programlisting -Any prefix on the function's name which follows the package naming format -creates the function and inserts the function into the appropriate namespace, -but not the current namespace. Because Perl 5 packages are open for -modification at any point, you may do this even if the namespace does not yet -exist, or if you have already declared functions in that namespace. - -You may only declare one function of the same name per namespace. Otherwise -Perl 5 will warn you about subroutine redefinition. If you're certain you want -to I an existing function, disable this warning with C. +This will declare the function and create the namespace as necessary. Remember +that Perl 5 packages are open for modification at any point. You may only +declare one function of the same name per namespace. Otherwise Perl 5 will warn +you about subroutine redefinition. Disable this warning with C--if you're certain this is what you intend. -You may call functions in other namespaces by using their fully-qualified -names: +Call functions in other namespaces with their fully-qualified names: =begin programlisting @@ -364,11 +358,10 @@ names: =end programlisting -Functions in namespaces are I outside of those namespaces in the sense -that you can refer to them directly, but they are only I by their -short names from within the namespace in which they are declared--unless you -have somehow made them available to the current namespace through the processes -of importing and exporting (L). +Functions in namespaces are I outside of those namespaces through +their fully-qualified names. Within a namespace, you may use the short name to +call any function declared in that namespace. You may also import names from +other namespaces. =head2 Importing @@ -378,11 +371,10 @@ X X> When loading a module with the C builtin (L), Perl automatically -calls a method named C on the provided package name. Modules with -procedural interfaces can provide their own C which makes some or all -defined symbols available in the calling package's namespace. Any arguments -after the name of the module in the C statement get passed to the module's -C method. Thus: +calls a method named C on that module. Modules can provide their own +C which makes some or all defined symbols available to the calling +package. Any arguments after the name of the module in the C statement get +passed to the module's C method. Thus: =begin programlisting @@ -403,8 +395,8 @@ arguments, while: ... loads the F module, calls C<< strict->import( 'refs' ) >>, then calls C<< strict->import( 'subs', vars' ) >>. -You may call a module's C method directly. The previous code example -is equivalent to: +C has special behavior with regard to C, but you may call +C directly. The C example is equivalent to: =begin programlisting @@ -417,23 +409,22 @@ is equivalent to: =end programlisting -Be aware that the C builtin adds an implicit C block around these -statements so that the C call happens I after the parser -has compiled the entire statement. This ensures that any imported symbols are -visible when compiling the rest of the program. Otherwise, any functions -imported from other modules but not declared in the current file would look -like undeclared barewords and C would complain. +The C builtin adds an implicit C block around these statements so +that the C call happens I after the parser has compiled +the entire C statement. This ensures that any imported symbols are visible +when compiling the rest of the program. Otherwise, any functions I +from other modules but not I in the current file would look like +barewords, and would violate C. =head1 Reporting Errors Z X> -Within a function, you can get information about the context of the call with -the C operator. If passed no arguments, it returns a three element -list containing the name of the calling package, the name of the file -containing the call, and the line number of the package on which the call -occurred: +Within a function, inspect the context of the call to the function with the +C builtin. When passed no arguments, it returns a three element list +containing the name of the calling package, the name of the file containing the +call, and the line number of the file on which the call occurred: =begin programlisting @@ -454,16 +445,15 @@ occurred: =end programlisting -You may pass a single, optional integer argument to C. If provided, -Perl will look back through the caller of the caller of the caller that many -times and provide information about that particular call. In other words, if -C used C, it would receive information -about the call from C. If it used C, it would receive -information about the call from the start of the program. +The full call chain is available for inspection. Pass a single integer argument +I to C to inspect the caller of the caller of the caller I +times. In other words, if C used C, it +would receive information about the call from C. If it used +C, it would receive information about the call from the start of the +program. -While providing this optional parameter lets you inspect the callers of -callers, it also provides more return values, including the name of the -function and the context of the call: +This optional argument also tells C to provide additional return +values, including the name of the function and the context of the call: =begin programlisting @@ -480,14 +470,13 @@ X; C> X; C> The standard C module uses this technique to great effect for reporting -errors and throwing warnings in functions; its C throws an exception -reported from the file and line number of its caller. When used in place of -C in library code, C can throw an exception due to incorrect -usage from the point of use. C's C function reports a warning -from the file and line number of its caller (L). +errors and throwing warnings in functions. When used in place of C in +library code, C throws an exception from the point of view of its +caller. C reports a warning from the file and line number of its caller +(L). This behavior is most useful when validating parameters or preconditions of a -function, when you want to indicate that the calling code is wrong somehow: +function to indicate that the calling code is wrong somehow: =begin programlisting @@ -505,10 +494,10 @@ function, when you want to indicate that the calling code is wrong somehow: =head2 Validating Arguments -Defensive programming often benefits from checking types and values of -arguments for appropriateness before further execution. By default, Perl 5 -provides few built-in mechanisms for doing so. To check that the I of -parameters passed to a function is correct, evaluate C<@_> in scalar context: +While Perl does its best to do what the programmer means, it offers few native +ways to test the validity of arguments provided to a function. Evaluate C<@_> +in scalar context to check that the I of parameters passed to a +function is correct: =begin programlisting @@ -522,13 +511,15 @@ parameters passed to a function is correct, evaluate C<@_> in scalar context: =end programlisting +X> + Type checking is more difficult, because of Perl's operator-oriented type -conversions (L). In cases where you need more strictness, -consider the CPAN module C. +conversions (L). The CPAN module C offers +more strictness. =head1 Advanced Functions -Functions may seem simple, but you can do much, much more with them. +Functions are the foundation of many advanced Perl features. =head2 Context Awareness @@ -537,10 +528,10 @@ X> X> Perl 5's builtins know whether you've invoked them in void, scalar, or list -context. So too can your functions know their calling contexts. The -misnamedN to verify.> C builtin returns -C to signify void context, a false value to signify scalar context, and -a true value to signify list context. +context. So too can your functions. The misnamedN +to verify.> C builtin returns C to signify void context, a +false value to signify scalar context, and a true value to signify list +context. =begin programlisting @@ -559,20 +550,20 @@ a true value to signify list context. =end programlisting This can be useful for functions which might produce expensive return values to -avoid doing so in void context. Some idiomatic functions return a list in list -context and an array reference in scalar context (or the first element of the -list). Even so, there's no single best recommendation for the use or avoidance -of C; sometimes it's clearer to write separate functions which -clearly indicate their expected uses and return values. +avoid doing so in void context. Some idiomatic functions return a list in list +context and the first element of the list or an array reference in scalar +context. Yet remember that there exists no single best recommendation for the +use C. Sometimes it's clearer to write separate and unambiguous +functions. =begin sidebar X> X> -With that said, Robin Houston's C and Damian Conway's -C distributions from the CPAN offer many possibilities for -writing powerful and usable interfaces. +robin Houston's C and Damian Conway's C distributions +from the CPAN offer many possibilities for writing powerful and usable +context-aware interfaces. =end sidebar @@ -583,23 +574,14 @@ X X X -Every call to a function in Perl creates a new I. This is an -internal data structure which represents the data for the call itself: incoming -parameters, the point to which to return, and all of the other call frames up -to the current point. It also captures the lexical environment of the specific -and current invocation of the function. This means that a function can -I; it can call itself. - -Recursion is a deceptively simple concept, but it can seem daunting if you -haven't encountered it before. Suppose you want to find an element in a sorted -array. You I iterate through every element of the array individually, -looking for the target, but on average, you'll have to examine half of the -elements of the array. +Suppose you want to find an element in a sorted array. You I iterate +through every element of the array individually, looking for the target, but on +average, you'll have to examine half of the elements of the array. Another +approach is to halve the array, pick the element at the midpoint, compare, then +repeat with either the lower or upper half. Divide and conquer. When you run +out of elements to inspect or find the element, stop. -Another approach is to halve the array, pick the element at the midpoint, -compare, then repeated with either the lower or upper half. You can write this -in a loop yourself or you could let Perl manage all of the state and tracking -necessary with a recursive function something like: +An automated test for this technique could be: =begin programlisting @@ -617,6 +599,18 @@ necessary with a recursive function something like: ok elem_exists( 48, @elements ), 'found end of lower half element'; ok elem_exists( 997, @elements ), 'found start of upper half element'; +=end programlisting + +Recursion is a deceptively simple concept. Every call to a function in Perl +creates a new I, an internal data structure which represents the +call itself, including the lexical environment of the function's current +invocation. This means that a function can call itself, or I. + +To make the previous test pass, write a function called C which +knows how to call itself, halving the list each time: + +=begin programlisting + sub elem_exists { my ($item, @array) = @_; @@ -644,18 +638,16 @@ necessary with a recursive function something like: =end programlisting -This isn't necessarily the best algorithm for searching a sorted list, but it -demonstrates recursion. Again, you I write this code in a procedural way, -but some algorithms are much clearer when written recursively. +While you I write this code in a procedural way and manage the halves of +the list yourself, this recursive approach lets Perl manage the bookkeeping. =head2 Lexicals Every new invocation of a function creates its own I of a lexical -scope. In the case of the recursive example, even though the declaration of -C creates a single scope for the lexicals C<$item>, C<@array>, -C<$midpoint>, and C<$miditem>, every I to C, even -recursively, has separate storage for the values of those lexical variables. -You can demonstrate that by adding debugging code to the function: +scope. Even though the declaration of C creates a single scope +for the lexicals C<$item>, C<@array>, C<$midpoint>, and C<$miditem>, every +I to C--even recursively--stores the values of those +lexicals separately. Demonstrate that with some debugging code: =begin programlisting @@ -673,8 +665,8 @@ You can demonstrate that by adding debugging code to the function: =end programlisting -The output demonstrates that not only can C call itself safely, -but the lexical variables do not interfere with each other. +Not only can C call itself, but the lexical variables of each +invocation are safe and separate. =head2 Tail Calls @@ -682,32 +674,20 @@ Z X One I of recursion is that you must get your return conditions -correct, or else your function will call itself an infinite number of times. -This is why the C function has several C statements. - -Perl offers a helpful warning when it detects what might be runaway recursion: -C. The limit is 100 recursive calls, which can -be too few in certain circumstances but too many in others. Disable this -warning with C in the scope of the recursive call. +correct, lest your function call itself an infinite number of times. +C function has several C statements for this reason. -Because each call to a function requires a new call frame, as well as space for -the call to store its own lexical values, highly-recursive code can use more -memory than iterative code. A feature called I can -help. +Perl offers a helpful C warning when it suspects +runaway recursion. The limit of 100 recursive calls is arbitrary, but often +useful. Disable this warning with C in the scope of +the recursive call. -=begin sidebar - -Tail call elimination may be most obvious when writing recursive code, but it -can be useful in any case of a tail call. Many programming language -implementations support automatic tail call elimination. - -=end sidebar - -X -X +Because each call to a function requires a new call frame and lexical storage +space, highly-recursive code can use more memory than iterative code. I can help. A I is a call to a function which directly returns that function's -results. The lines: +results. These recursive calls to C: =begin programlisting @@ -720,50 +700,29 @@ results. The lines: =end programlisting -... which return the results of the recursive C calls directly, -are candidates for tail call elimination. This elimination avoids returning to -the current call and then returning to the parent call. Instead, it returns to -the parent call directly. - -X> - -Perl 5 supports manual tail call elimination, but Yuval Kogman's -C is worth exploring if you find yourself with highly -recursive code or code that could benefit from tail call elimination. -C is appropriate for tail calls of non-recursive code: +... are candidates for tail call elimination. This optimization would avoid +returning to the current call and then returning to the parent call. Instead, +it returns to the parent call directly. -=begin programlisting - - use Sub::Call::Tail; - - sub log_and_dispatch - { - my ($dispatcher, $request) = @_; - warn "Dispatching with $dispatcher\n"; - - return dispatch( $dispatcher, $request ); - } - -=end programlisting - -In this example, you can replace the C with the new C keyword -with no functional changes (yet more clarity and improved performance): +=begin sidebar -=begin programlisting +X> - B dispatch( $dispatcher, $request ); +Yuval Kogman's experimental C can help with tail calls of +non-recursive code. -=end programlisting +=end sidebar X> X> X> -If you really I eliminate tail calls, use a special form of the C -builtin. Unlike the form which can often lead to spaghetti code, the C -function form replaces the current function call with a call to another -function. You may use a function by name or by reference. You must always set -C<@_> yourself manually, if you want to pass different arguments: +Unfortunately, Perl 5 does not eliminate tail calls automatically. You can do +so manually with a special form of the C builtin. Unlike the form which +can often lead to spaghetti code, the C function form replaces the +current function call with a call to another function. You may use a function +by name or by reference. You must always set C<@_> yourself manually, if you +want to pass different arguments: =begin programlisting @@ -783,15 +742,14 @@ C<@_> yourself manually, if you want to pass different arguments: =end programlisting -The comparative cleanliness of the CPAN versions is obvious. +Sometimes optimizations are ugly. =head1 Pitfalls and Misfeatures X -Not all features of Perl 5 functions are always helpful. In particular, -prototypes (L) rarely do what you mean. They have their uses, but -you can avoid them outside of a few cases. +Other Perl 5 function features look good, but have substantial drawbacks. In +particular, prototypes (L) rarely do what novices mean. X X @@ -800,16 +758,16 @@ X> X> Perl 5 still supports old-style invocations of functions, carried over from -older versions of Perl. While you may now invoke Perl functions by name, +older versions of Perl. While you may now invoke Perl functions by name, previous versions of Perl required you to invoke them with a leading ampersand -(C<&>) character. Perl 1 required you to use the C builtin: +(C<&>) character. Perl 1 required you to use the C builtin: =begin programlisting # outdated style; avoid my $result = &calculate_result( 52 ); - # Perl 1 style + # Perl 1 style; avoid my $result = do calculate_result( 42 ); # crazy mishmash; really truly avoid @@ -818,16 +776,15 @@ previous versions of Perl required you to invoke them with a leading ampersand =end programlisting While the vestigial syntax is visual clutter, the leading ampersand form has -other surprising behaviors. First, it disables prototype checking (as if that -often mattered). Second, if you do not pass arguments explicitly, it -I passes the contents of C<@_> unmodified. Both can lead to -surprising behavior. +other surprising behaviors. First, it disables any prototype checking. Second, +it I passes the contents of C<@_> unmodified unless you specify +arguments yourself. Both can lead to surprising behavior. -A final pitfall comes from leaving the parentheses off of function calls. The -Perl 5 parser uses several heuristics to resolve ambiguity of barewords and the -number of parameters passed to a function, but occasionally those heuristics -guess wrong. While it's often wise to remove extraneous parentheses, compare -the readability of these two lines of code: +A final pitfall comes from leaving the parentheses off of function calls. The +Perl 5 parser uses several heuristics to resolve ambiguous barewords and the +number of parameters passed to a function, and heuristics can get things wrong. +While extraneous parentheses can hamper readability, consider readability +versus correctness: =begin programlisting @@ -838,12 +795,10 @@ the readability of these two lines of code: =end programlisting -The subtle bug in the second form is that the call to C will -gobble up the test description intended as the second argument to C. -Because C uses a slurpy second parameter, this may go unnoticed -until Perl produces warnings about comparing a non-number (the test -description, which it cannot convert into a number) with the element in the -array. +In the second form, the call to C will gobble up the test +description intended as the second argument to C. Because +C uses a slurpy second parameter, this may go unnoticed until +Perl produces warnings about comparing a non-number (the test description, +which it cannot convert into a number) with the element in the array. -This is admittedly an extreme case, but it is a case where proper use of -parentheses can clarify code and make subtle bugs obvious to the reader. +Thoughtful use of parentheses can clarify code and make subtle bugs unlikely. diff --git a/sections/implicit_ideas.pod b/sections/implicit_ideas.pod index 893c3ddd..8a2928ab 100644 --- a/sections/implicit_ideas.pod +++ b/sections/implicit_ideas.pod @@ -195,15 +195,17 @@ Use C<$_> as you would the word "it" in formal writing: sparingly, in small and =head2 The Default Array Variables +Z + X X> X> X> Perl also provides two implicit array variables. Perl passes arguments to -functions in an array named C<@_>. Array manipulation operations (L) -inside functions affect this array by default, so these two snippets of code -are equivalent: +functions (L) in an array named C<@_>. Array manipulation operations +(L) inside functions affect this array by default, so these two +snippets of code are equivalent: =begin programlisting diff --git a/sections/scope.pod b/sections/scope.pod index 60f1fd88..6c7df600 100644 --- a/sections/scope.pod +++ b/sections/scope.pod @@ -5,10 +5,10 @@ Z X X -I in Perl refers to the lifespan and visibility of symbols. Everything -with a name in Perl (a variable, a function) has a scope. Scoping helps to -enforce I--keeping related concepts together and preventing them -from leaking out. +I in Perl refers to the lifespan and visibility of named entities. +Everything with a name in Perl (a variable, a function) has a scope. Scoping +helps to enforce I--keeping related concepts together and +preventing them from leaking out. =head2 Lexical Scope @@ -17,20 +17,20 @@ Z X X -The most common form of scoping in modern Perl is lexical scoping. The Perl -compiler resolves this scope during compilation. This scope is visible as you -I a program. A block delimited by curly braces creates a new scope, -whether a bare block, the block of a loop construct, the block of a C -declaration, an C block, or any other non-quoting block: +L is the scope visible as you I a program. The Perl +compiler resolves this scope during compilation. A block delimited by curly +braces creates a new scope, whether a bare block, the block of a loop +construct, the block of a C declaration, an C block, or any other +non-quoting block: =begin programlisting # outer lexical scope { - package My::Class; + package Robot::Butler; # inner lexical scope - sub awesome_method + sub tidy_room { # further inner lexical scope do { @@ -49,32 +49,32 @@ declaration, an C block, or any other non-quoting block: X -Lexical scope governs the visibility of variables declared with C; these -are I variables. A lexical variable declared in one scope is visible -in that scope and any scopes nested within it, but is invisible to sibling or -outer scopes. Thus, in the code: +Lexical scope governs the visibility of variables declared with C-- +I variables. A lexical variable declared in one scope is visible in +that scope and any scopes nested within it, but is invisible to sibling or +outer scopes: =begin programlisting # outer lexical scope { - package My::Class; + package Robot::Butler - my $outer; + my $battery_level; - sub awesome_method + sub tidy_room { - my $inner; + my $timer; do { - my $do_scope; + my $dustpan; ... } while (@_); # sibling inner lexical scope for (@_) { - my $for_scope; + my $polish_cloth; ... } } @@ -82,9 +82,9 @@ outer scopes. Thus, in the code: =end programlisting -... C<$outer> is visible in all four scopes. C<$inner> is visible in the -method, the C block, and the C loop. C<$do_scope> is visible only in -the C block and C<$for_scope> within the C loop. +... C<$battery_level> is visible in all four scopes. C<$timer> is visible in +the method, the C block, and the C loop. C<$dustpan> is visible only +in the C block and C<$polish_cloth> within the C loop. X X @@ -107,23 +107,21 @@ outer scope hides, or I, the outer lexical: =end programlisting -This program prints C and then CN. Even though redeclaring a lexical variable with -the same name and type in a single lexical scope produces a warning message, -shadowing a lexical in a nested scope does not; this is a feature of lexical -shadowing. +This program prints C and then CN. Even though redeclaring a lexical variable with the same name and +type I produces a warning message, shadowing a +lexical. This is a feature of encapsulation. =begin sidebar -Lexical shadowing can happen by accident, but if you limit the scope of -variables and limit the nesting of scopes--as is good design anyhow--you lessen -your risk. +Lexical shadowing can happen by accident. Limit the scope of variables and the +nesting of scopes to lessen your risk. =end sidebar -Lexical declaration has its subtleties. For example, a lexical variable used -as the iterator variable of a C loop has a scope I the loop block. -It is not visible outside the block: +Some lexical declarations have subtleties, such as a lexical variable used as +the iterator variable of a C loop. Its declaration comes outside of the +block, but its scope is that I the loop block: =begin programlisting @@ -141,8 +139,8 @@ It is not visible outside the block: X X -Similarly, the C construct creates a I (akin to C) -within its block: +Similarly, C (L) creates a I (akin to C) within its block: =begin programlisting @@ -151,19 +149,17 @@ within its block: given ('inner') { say; - $_ = 'whomped inner'; + $_ = 'this assignment does nothing useful'; } say; =end programlisting -... despite assignment to C<$_> inside the block. You may explicitly -lexicalize the topic yourself, though this is more useful when considering -dynamic scope. +... such that leaving the block restores the previous value of C<$_>. -Finally, lexical scoping facilitates closures (L). Beware creating -closures accidentally. +Functions--named and anonymous--provide lexical scoping to their bodies. This +facilitates closures (L). =head3 Our Scope @@ -172,13 +168,13 @@ X> X X -Within a given scope, you may declare an alias to a package variable with the -C builtin. Like C, C enforces lexical scoping--of the alias. -The fully-qualified name is available everywhere, but the lexical alias is -visible only within its scope. +Within given scope, declare an alias to a package variable with the C +builtin. Like C, C enforces lexical scoping of the alias. The +fully-qualified name is available everywhere, but the lexical alias is visible +only within its scope. -The best use of C is for variables you absolutely I have, such as -C<$VERSION>. +C<$our> is most useful with package global variables such as C<$VERSION> and +C<$AUTOLOAD>. =head2 Dynamic Scope @@ -188,8 +184,9 @@ X X Dynamic scope resembles lexical scope in its visibility rules, but instead of -looking outward in compile-time scopes, lookup happens along the current -calling context. Consider the example: +looking outward in compile-time scopes, lookup traverses backwards through the +calling context. While a package global variable may be I within all +scopes, its I changes depending on Cization and assignment: =begin programlisting @@ -223,42 +220,34 @@ calling context. Consider the example: =end programlisting The program begins by declaring an C variable, C<$scope>, as well as three -functions. It ends by assigning to C<$scope> and calling C. +functions. It ends by assigning to C<$scope> and calling C. X> Within C, the program prints C<$scope>'s current value, C, -then Cizes the variable. This changes the visibility of the symbol +then Cizes the variable. This changes the visibility of the symbol within the current lexical scope I in any functions called from the -current lexical scope. Thus, C<$scope> contains C within the -body of both C and C. After C returns--at the point -of exiting the block containing the Cization of C<$scope>, Perl restores -the original value of the variable. The final C prints C -once again. - -While the variable is I within all scopes, the I of the -variable changes depending on Cization and assignment. This feature can -be tricky and subtle, but it is especially useful for changing the values of -magic variables. +I lexical scope. Thus, C<$scope> contains C within the +body of both C and C. After C returns, when control +flow reaches the end of its block, Perl restores the original value of the +Cized C<$scope>. The final C prints C once again. X X X -This difference in visibility between package variables and lexical variables -is apparent in the different storage mechanisms of these variables within Perl -5 itself. Every scope which contains lexical variables has a special data -structure called a I or I which can store the values for -its enclosed lexical variables. Every time control flow enters one of these -scopes, Perl creates another lexpad for the values of those lexical variables -for that particular call. (This is how a function can call itself and not -clobber the values of existing variables.) - -Package variables have a storage mechanism called symbol tables. Each package -has a single symbol table, and every package variable has an entry in this -table. You can inspect and modify this symbol table from Perl; this is how -importing works (L). This is also why you may only Cize -global and package global variables and never lexical variables. +Package variables and lexical variables have different visibility rules and +storage mechanisms within Perl. Every scope which contains lexical variables +has a special data structure called a I or I which can +store the values for its enclosed lexical variables. Every time control flow +enters one of these scopes, Perl creates another lexpad for the values of those +lexical variables for that particular call. This makes functions work +correctly, especially in recursive calls (L). + +Each package has a single I which holds package variables as well +as named functions. Importing (L) works by inspecting and +manipulating this symbol table. So does C. You may only Cize +global and package global variables--never lexical variables. X> X> @@ -269,17 +258,16 @@ X> X> X> -It's common to Cize several magic variables. For example, C<$/>, the +C is most often useful with magic variables. For example, C<$/>, the input record separator, governs how much data a C operation will read -from a filehandle. C<$!>, the system error variable, contains the error number -of the most recent system call. C<$@>, the Perl C error variable, -contains any error from the most recent C operation. C<$|>, the -autoflush variable, governs whether Perl will flush the currently Ced filehandle +after every write operation. -These are all special global variables; Cizing them in the narrowest -possible scope will avoid the action at a distance problem of modifying global -variables used other places in your code. +Cizing these in the narrowest possible scope limits the effect of your +changes. This can prevent strange behavior in other parts of your code. =head2 State Scope @@ -289,10 +277,9 @@ X> X> X -A final type of scope is new as of Perl 5.10. This is the scope of the -C builtin. State scope resembles lexical scope in that it declares a -lexical variable, but the value of that variable gets initialized I, and -then persists: +Perl 5.10 added a new scope to support the C builtin. State scope +resembles lexical scope in terms of visibility, but adds a one-time +initialization as well as value persistence: =begin programlisting @@ -308,11 +295,12 @@ then persists: =end programlisting -On the first call to state, C<$count> has never been initialized, so Perl -executes the assignment. The program prints C<1>, C<2>, and C<3>. If you -change C to C, the program will print C<1>, C<1>, and C<1>. +On the first call to C, Perl performs its single initialization of +C<$count>. On subsequent calls, C<$count> retains its previous value. This +program prints C<1>, C<2>, and C<3>. Change C to C and the program +will print C<1>, C<1>, and C<1>. -You may also use an incoming parameter to set the initial value of the C variable: +You may use an expression to set a C variable's initial value: =begin programlisting @@ -330,9 +318,11 @@ You may also use an incoming parameter to set the initial value of the C Even though a simple reading of the code may suggest that the output should be C<2>, C<4>, and C<6>, the output is actually C<2>, C<3>, and C<4>. The first -call to the sub C sets the C<$count> variable. Subsequent calls will -not change its value. This behavior is as intended and documented, though its -implementation can lead to surprising results: +call to the sub C sets the C<$count> variable. Subsequent calls will +not change its value. + +C can be useful for establishing a default value or preparing a cache, +but be sure to understand its initialization behavior if you use it: =begin programlisting @@ -351,9 +341,23 @@ implementation can lead to surprising results: The counter for this program prints C<2>, C<3>, and C<4> as expected, but the values of the intended second arguments to the C calls are C, -C<4>, and C<6>--not because the integers are the second arguments passed, but -because the C of the first argument only happens in the first call to -C. +C<4>, and C<6>--because the C of the first argument only happens in the +first call to C. Either change the API to prevent this mistake, or +guard against it with: -C can be useful for establishing a default value or preparing a cache, -but be sure to understand its initialization behavior if you use it. +=begin programlisting + + sub counter + { + my ($initial_value, $text) = @_; + + state $count = $initial_value; + say "Second arg is: $text"; + return $count++; + } + + say counter(2, 'two'); + say counter(4, 'four'); + say counter(6, 'six'); + +=end programlisting