diff --git a/sections/anonymous_functions.pod b/sections/anonymous_functions.pod index a5c40376..eae037a5 100644 --- a/sections/anonymous_functions.pod +++ b/sections/anonymous_functions.pod @@ -23,7 +23,6 @@ input with behavior: plus => \&add_two_numbers, minus => \&subtract_two_numbers, times => \&multiply_two_numbers, - # ... and so on ); sub add_two_numbers { $_[0] + $_[1] } @@ -34,8 +33,7 @@ input with behavior: { my ($left, $op, $right) = @_; - die "Unknown operation!" - unless exists $dispatch{ $op }; + return unless exists $dispatch{ $op }; return $dispatch{ $op }->( $left, $right ); } @@ -72,8 +70,8 @@ This dispatch table offers some degree of security; only those functions mapped within the table are available for users to call. If your dispatch function blindly assumed that the string given as the name of the operator corresponded directly to the name of a function to call, a malicious user could conceivably -call any function in any other namespace by crafting an operator name of -C<'Internal::Functions::some_malicious_function'>. +call any function in any other namespace by passing +C<'Internal::Functions::malicious_function'>. =end tip @@ -93,7 +91,7 @@ You may also see anonymous functions passed as function arguments: } invoke_anon_function( \&named_func ); - invoke_anon_function( B ); + invoke_anon_function( B ); =end programlisting @@ -110,8 +108,8 @@ named or anonymous with introspection: sub show_caller { - my ($package, $filename, $line, $sub) = caller(1); - say "Called from $sub in $package at $filename : $line"; + my ($package, $file, $line, $sub) = caller(1); + say "Called from $sub in $package:$file:$line"; } sub main @@ -136,8 +134,10 @@ introspection. The result may be surprising: - Called from ShowCaller::B
in ShowCaller at anoncaller.pl : 20 - Called from ShowCaller::B<__ANON__> in ShowCaller at anoncaller.pl : 17 + Called from ShowCaller::B
+ in ShowCaller:anoncaller.pl:20 + Called from ShowCaller::B<__ANON__> + in ShowCaller:anoncaller.pl:17 X> @@ -193,8 +193,8 @@ functions. Consider the CPAN module C: my $croaker = exception { die 'I croak!' }; my $liver = exception { 1 + 1 }; - like( $croaker, qr/I croak/, 'die() should throw an exception' ); - is( $liver, undef, 'simple addition should not' ); + like( $croaker, qr/I croak/, 'die() should croak' ); + is( $liver, undef, 'addition should live' ); done_testing(); @@ -217,14 +217,13 @@ This syntax allows you to pass named functions by reference as well: =begin programlisting B - B my $croaker = exception \&croaker; my $liver = exception \&liver; - like( $croaker, qr/I croak/, 'die() should throw an exception' ); - is( $liver, undef, 'simple addition should not' ); + like( $croaker, qr/I croak/, 'die() should die' ); + is( $liver, undef, 'addition should live' ); =end programlisting @@ -247,8 +246,8 @@ will contain, and so will throw an exception. =begin screen - Type of arg 1 to Test::Fatal::exception must be block or sub {} - (not private variable) + Type of arg 1 to Test::Fatal::exception + must be block or sub {} (not private variable) =end screen @@ -260,7 +259,8 @@ multiple arguments cannot have a trailing comma after the function block: use Test::More; use Test::Fatal 'dies_ok'; - dies_ok { die 'This is my boomstick!' } 'No movie references here'; + dies_ok { die 'This is my boomstick!' } + 'No movie references here'; =end programlisting diff --git a/sections/autoload.pod b/sections/autoload.pod index 1b008313..8649b038 100644 --- a/sections/autoload.pod +++ b/sections/autoload.pod @@ -230,6 +230,13 @@ C with the C pragma: =end programlisting +=begin tip Now You See Them + +Forward declarations are only useful in the two rare cases of attributes and +autoloading (L). + +=end tip + That technique has the advantage of documenting your intent but the disadvantage that you have to maintain a static list of functions or methods. Overriding C (L) sometimes works better: @@ -288,7 +295,8 @@ error: =begin screen - Use of inherited AUTOLOAD for non-method I() is deprecated + Use of inherited AUTOLOAD for non-method + I() is deprecated =end screen diff --git a/sections/closures.pod b/sections/closures.pod index dc17a246..458d753d 100644 --- a/sections/closures.pod +++ b/sections/closures.pod @@ -20,16 +20,11 @@ You've probably already created and used closures without realizing it: =begin programlisting - { - package Invisible::Closure; + package Invisible::Closure; - my $filename = shift @ARGV; + my $filename = shift @ARGV; - sub get_filename - { - return $filename; - } - } + sub get_filename { return $filename } =end programlisting @@ -55,8 +50,9 @@ invoked, will return the next item in the iteration: } } - my @cousins = qw( Rick Alex Kaycee Eric Corey Mandy Christine ) - my $cousins = make_iterator( @cousins ); + my $cousins = make_iterator( + qw( Rick Alex Kaycee Eric Corey Mandy Christine ) + ); say $cousins->() for 1 .. 5; @@ -73,14 +69,12 @@ lexical environment: =begin programlisting - my @aunts = qw( Carole Phyllis Wendy Sylvia Monica Lupe ); - my $cousins = make_iterator( @cousins ); - my $aunts = make_iterator( @aunts ); + my $aunts = make_iterator( + qw( Carole Phyllis Wendy Sylvia Monica Lupe ) + ); say $cousins->(); say $aunts->(); - say $cousins->(); - say $aunts->(); =end programlisting @@ -140,10 +134,10 @@ elements you need: { for my $calc (@fibs .. $item) { - $fibs[$calc] = $fibs[$calc - 2] + $fibs[$calc - 1]; + $fibs[$calc] = $fibs[$calc - 2] + + $fibs[$calc - 1]; } } - return $fibs[$item]; } } @@ -162,8 +156,7 @@ returns the calculated or cached value. If you extract the behavior specific to Fibonacci values, this code can provide any arbitrary code with a lazily-iterated cache. -Extract the function C and rewrite C to -use it: +Extracting the function C produces: =begin programlisting @@ -175,12 +168,25 @@ use it: { my $item = shift; - $calc_element->($item, \@cache) unless $item < @cache; + $calc_element->($item, \@cache) + unless $item < @cache; return $cache[$item]; }; } +=end programlisting + +=begin tip Fold, Apply, and Filter + +In one sense, the builtins C, C, and C are themselves +higher-order functions. Compare them to C. + +=end tip + +Now C can become: + +=begin programlisting sub gen_fib { @@ -193,7 +199,8 @@ use it: for my $calc ((@$fibs - 1) .. $item) { - $fibs->[$calc] = $fibs->[$calc - 2] + $fibs->[$calc - 1]; + $fibs->[$calc] = $fibs->[$calc - 2] + + $fibs->[$calc - 1]; } }, @fibs @@ -206,14 +213,7 @@ The program behaves as it did before, but the use of function references and closures separates the cache initialization behavior from the calculation of the next number in the Fibonacci series. Customizing the behavior of code--in this case, C--by passing in a function allows tremendous -flexibility and abstraction. - -=begin tip Fold, Apply, and Filter - -In one sense, the builtins C, C, and C are themselves -higher-order functions. Compare them to C. - -=end tip +flexibility. =head2 Closures and Partial Application @@ -243,8 +243,12 @@ arguments that never change. X -I binds some of the arguments to a function and allows you -to provide the rest later: +A technique called I allows you to bind I of the +arguments to a function so that you can provide the others later. Wrap the +function you intend to call in a closure and pass the bound arguments. + +Consider an ice cream cart which only serves French Vanilla ice cream over +Cavendish bananas: =begin programlisting diff --git a/sections/functions.pod b/sections/functions.pod index be5087d2..7c4173ab 100644 --- a/sections/functions.pod +++ b/sections/functions.pod @@ -27,13 +27,6 @@ will define it later: =end programlisting -=begin tip Now You See Them - -Forward declarations are only useful in the two rare cases of attributes -(L) and autoloading (L). - -=end tip - =head1 Invoking Functions X @@ -94,23 +87,7 @@ operate on C<@_> directly: =end programlisting -C<@_> behaves as a normal Perl array. Refer to individual elements by index: - -=begin programlisting - - sub greet_one_indexed - { - B; - say "Hello, $name!"; - - # or, less clear - say "Hello, $_[0]!"; - } - -=end programlisting - -... or C, C, C, C, C, and slice C<@_>. Most -code uses C or list unpacking: +Most code uses C or list unpacking, though C<@_> behaves as a normal Perl array, so you can refer to individual elements by index: =begin programlisting @@ -120,14 +97,25 @@ code uses C or list unpacking: say "Hello, $name!"; } - sub greet_two_shift + sub greet_two_no_shift { my ($hero, $sidekick) = @_; say "Well if it isn't $hero and $sidekick. Welcome!"; } + sub greet_one_indexed + { + B; + say "Hello, $name!"; + + # or, less clear + say "Hello, $_[0]!"; + } + =end programlisting +... or C, C, C, C, C, and slice C<@_>. + =begin tip The Implicit Them Remember that the array builtins use C<@_> as the default operand I; # buggy - say "Hello, $name; you're looking quite numeric today!" + say "Hello, $name; you look numeric today!" } =end programlisting @@ -203,9 +191,10 @@ X> X> X> -The C, C, C, and -C CPAN modules offer more powerful parameter -handling. +Several CPAN distributions extend Perl 5's parameter handling with additional +syntax and options. C and C are powerful. +C is basic, but useful. +C works very well with Moose (L). =end tip @@ -327,7 +316,7 @@ Be cautious, and unpack C<@_> rigorously. =head1 Functions and Namespaces Every function has a containing namespace (L). Functions in an -undeclared namespace--functions not declared after an explicit C +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: @@ -437,7 +426,7 @@ call, and the line number of the file on which the call occurred: sub show_call_information { my ($package, $file, $line) = caller(); - say "Called from $package in $file at $line"; + say "Called from $package in $file:$line"; } =end programlisting @@ -457,7 +446,7 @@ values, including the name of the function and the context of the call: sub show_call_information { my ($package, $file, $lineB<, $func>) = caller(B<0>); - say "Called B<$func> from $package in $file at $line"; + say "Called B<$func> from $package in $file:$line"; } =end programlisting @@ -473,21 +462,7 @@ 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 to indicate that the calling code is wrong somehow: - -=begin programlisting - - use Carp 'croak'; - - sub add_two_numbers - { - croak 'add_two_numbers() takes two and only two arguments' - unless @_ == 2; - - ... - } - -=end programlisting +function to indicate that the calling code is wrong somehow. =head2 Validating Arguments @@ -500,7 +475,7 @@ function is correct: sub add_numbers { - croak "Expected two numbers, but received: " . @_ + croak 'Expected two numbers, but received: ' . @_ unless @_ == 2; ... @@ -535,9 +510,10 @@ context. sub context_sensitive { my $context = wantarray(); - return qw( Called in list context ) if $context; - say 'Called in void context' unless defined $context; - return 'Called in scalar context' unless $context; + + return qw( List context ) if $context; + say 'Void context' unless defined $context; + return 'Scalar context' unless $context; } context_sensitive(); @@ -582,19 +558,32 @@ An automated test for this technique could be: =begin programlisting - use Test::More tests => 8; + use Test::More; + + my @elements = + ( + 1, 5, 6, 19, 48, 77, 997, 1025, 7777, 8192, 9999 + ); - my @elements = ( 1, 5, 6, 19, 48, 77, 997, 1025, 7777, 8192, 9999 ); + ok elem_exists( 1, @elements ), + 'found first element in array'; + ok elem_exists( 9999, @elements ), + 'found last element in array'; + ok ! elem_exists( 998, @elements ), + 'did not find element not in array'; + ok ! elem_exists( -1, @elements ), + 'did not find element not in array'; + ok ! elem_exists( 10000, @elements ), + 'did not find element not in array'; - ok elem_exists( 1, @elements ), 'found first element in array'; - ok elem_exists( 9999, @elements ), 'found last element in array'; - ok ! elem_exists( 998, @elements ), 'did not find element not in array'; - ok ! elem_exists( -1, @elements ), 'did not find element not in array'; - ok ! elem_exists( 10000, @elements ), 'did not find element not in array'; + ok elem_exists( 77, @elements ), + 'found midpoint element'; + ok elem_exists( 48, @elements ), + 'found end of lower half element'; + ok elem_exists( 997, @elements ), + 'found start of upper half element'; - ok elem_exists( 77, @elements ), 'found midpoint element'; - ok elem_exists( 48, @elements ), 'found end of lower half element'; - ok elem_exists( 997, @elements ), 'found start of upper half element'; + done_testing(); =end programlisting @@ -612,25 +601,28 @@ knows how to call itself, halving the list each time: { my ($item, @array) = @_; - # break recursion if there are no elements to search + # break recursion with no elements to search return unless @array; - # bias down, if there are an odd number of elements + # bias down with odd number of elements my $midpoint = int( (@array / 2) - 0.5 ); my $miditem = $array[ $midpoint ]; - # return true if the current element is the target + # return true if found return 1 if $item == $miditem; - # return false if the current element is the only element + # return false with only one element return if @array == 1; # split the array down and recurse - return B( $item, @array[0 .. $midpoint] ) - if $item < $miditem; - - # split the array up and recurse - return B( $item, @array[$midpoint + 1 .. $#array] ); + return B( + $item, @array[0 .. $midpoint] + ) if $item < $miditem; + + # split the array and recurse + return B( + $item, @array[ $midpoint + 1 .. $#array ] + ); } =end programlisting @@ -644,7 +636,10 @@ Every new invocation of a function creates its own I of a lexical 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: +lexicals separately. + +Not only can C call itself, but the lexical variables of each +invocation are safe and separate: =begin programlisting @@ -662,9 +657,6 @@ lexicals separately. Demonstrate that with some debugging code: =end programlisting -Not only can C call itself, but the lexical variables of each -invocation are safe and separate. - =head2 Tail Calls Z @@ -688,12 +680,15 @@ results. These recursive calls to C: =begin programlisting - # split the array down and recurse - return elem_exists( $item, @array[0 .. $midpoint] ) - if $item < $miditem; + # split the array down and recurse + return B( + $item, @array[0 .. $midpoint] + ) if $item < $miditem; - # split the array up and recurse - return elem_exists( $item, @array[$midpoint + 1 .. $#array] ); + # split the array and recurse + return B( + $item, @array[ $midpoint + 1 .. $#array ] + ); =end programlisting @@ -714,19 +709,19 @@ want to pass different arguments: =begin programlisting - # split the array down and recurse - if ($item < $miditem) - { - @_ = ($item, @array[0 .. $midpoint]); - B - } + # split the array down and recurse + if ($item < $miditem) + { + @_ = ($item, @array[0 .. $midpoint]); + B + } - # split the array up and recurse - else - { - @_ = ($item, @array[$midpoint + 1 .. $#array] ); - B - } + # split the array up and recurse + else + { + @_ = ($item, @array[$midpoint + 1 .. $#array] ); + B + } =end programlisting @@ -736,8 +731,8 @@ Sometimes optimizations are ugly. X -Other Perl 5 function features look good, but have substantial drawbacks. In -particular, prototypes (L) rarely do what novices mean. +Some Perl features look good, but have substantial drawbacks. In particular, +prototypes (L) rarely do what novices mean. X X @@ -770,23 +765,20 @@ 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 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: +number of parameters passed to a function. Heuristics can be wrong: =begin programlisting - ok( elem_exists( 1, @elements ), 'found first element in array' ); - # warning; contains a subtle bug - ok elem_exists 1, @elements, 'found first element in array'; + ok elem_exists 1, @elements, 'found first element'; =end programlisting -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. +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. -Thoughtful use of parentheses can clarify code and make subtle bugs unlikely. +While extraneous parentheses can hamper readability, thoughtful use of +parentheses can clarify code and make subtle bugs unlikely. diff --git a/sections/scope.pod b/sections/scope.pod index 7803702b..79ad72e6 100644 --- a/sections/scope.pod +++ b/sections/scope.pod @@ -21,38 +21,14 @@ I 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 Robot::Butler; - - # inner lexical scope - sub tidy_room - { - # further inner lexical scope - do { - ... - } while (@_); - - # sibling inner lexical scope - for (@_) - { - ... - } - } - } - -=end programlisting +non-quoting block. X -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: +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 @@ -60,13 +36,16 @@ outer scopes: { package Robot::Butler + # inner lexical scope my $battery_level; sub tidy_room { + # further inner lexical scope my $timer; do { + # innermost lexical scope my $dustpan; ... } while (@_); @@ -74,6 +53,7 @@ outer scopes: # sibling inner lexical scope for (@_) { + # separate innermost scope my $polish_cloth; ... } @@ -94,17 +74,15 @@ outer scope hides, or I, the outer lexical: =begin programlisting - { - my $name = 'Jacob'; - - { - my $name = 'Edward'; - say $name; - } + my $name = 'Jacob'; + { + my $name = 'Edward'; say $name; } + say $name; + =end programlisting This program prints C and then CN the loop block: =begin programlisting - my $cat = 'Bradley'; + my $cat = 'Brad'; - for my $cat (qw( Jack Daisy Petunia Tuxedo )) + for my $cat (qw( Jack Daisy Petunia Tuxedo Choco )) { say "Iterator cat is $cat"; } @@ -139,8 +117,8 @@ block, but its scope is that I the loop block: X X -Similarly, C (L) creates a I (akin to C) within its block: +Similarly, C (L) creates a I (like C) +within its block: =begin programlisting @@ -173,7 +151,7 @@ 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. -C is most useful with package global variables such as C<$VERSION> and +C is most useful with package global variables like C<$VERSION> and C<$AUTOLOAD>. =head2 Dynamic Scope @@ -190,32 +168,29 @@ scopes, its I changes depending on Cization and assignment: =begin programlisting - { - our $scope; - - sub inner - { - say $scope; - } + our $scope; - sub main - { - say $scope; - local $scope = 'main() scope'; - middle(); - } + sub inner + { + say $scope; + } - sub middle - { - say $scope; - inner(); - } + sub main + { + say $scope; + local $scope = 'main() scope'; + middle(); + } - $scope = 'outer scope'; - main(); + sub middle + { say $scope; + inner(); } + $scope = 'outer scope'; + main(); + say $scope; =end programlisting diff --git a/sections/state.pod b/sections/state.pod index bd38d2ae..12941f31 100644 --- a/sections/state.pod +++ b/sections/state.pod @@ -43,7 +43,8 @@ parlor. Every hundredth person gets free sprinkles: my $order = shift; - add_sprinkles($order) if ($cust_count % 100 == 0); + add_sprinkles($order) + if ($cust_count % 100 == 0); ... } @@ -67,7 +68,8 @@ between invocations: $cust_count++; my $order = shift; - add_sprinkles($order) if ($cust_count % 100 == 0); + add_sprinkles($order) + if ($cust_count % 100 == 0); ... } @@ -117,7 +119,7 @@ it: sub inadvertent_state { - # my $counter = 1 if 0; # DEPRECATED; do not use + # my $counter = 1 if 0; # DEPRECATED; don't use state $counter = 1; # use instead ...