Skip to content

Commit

Permalink
Edited chapter 9.
Browse files Browse the repository at this point in the history
  • Loading branch information
chromatic committed Oct 19, 2011
1 parent 1e169bf commit 15ee51f
Show file tree
Hide file tree
Showing 10 changed files with 671 additions and 616 deletions.
17 changes: 7 additions & 10 deletions sections/chapter_09.pod
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@

Z<writing_real_programs>

Writing simple example programs to solve example problems in a book helps you
learn a language in the small. Yet writing real programs requires more than
learning the syntax of a language, or its design principles, or even how to
find and use its libraries.

Practical programming requires you to manage code: to organize it, to know that
it works, to make it robust in the face of errors of logic or intent, and to do
all of this in a concise, clear, and maintainable fashion. Fortunately, modern
Perl provides many tools and techniques to write real programs--from testing to
the organization of your source code.
A book can teach you to write small programs to solve small example problems.
You can learn a lot of syntax that way. To write real programs to solve real
problems, you must learn to I<manage> code written in your language. How do you
organize code? How do you know that it works? How can you make it robust in the
face of errors? What makes code concise, clear, and maintainable?

Modern Perl provides many tools and techniques to write real programs.

L<testing>

Expand Down
140 changes: 70 additions & 70 deletions sections/code_generation.pod
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,24 @@

Z<code_generation>

Improving as a programmer requires you to search for better abstractions. The
less code you have to write, the better. The more general your solutions, the
better. When you can delete code and add features, you've achieved something
great.

Novice programmers write more code than they need to write, partly from
unfamiliarity with their languages, libraries, and idioms, but also due to
inexperience creating and maintaining good abstractions. They start by writing
long lists of procedural code, then discover functions, then parameters, then
objects, and--perhaps--higher-order functions and closures.
unfamiliarity with languages, libraries, and idioms, but also due to
inexperience. They start by writing long lists of procedural code, then
discover functions, then parameters, then objects, and--perhaps--higher-order
functions and closures.

As you become a better programmer, you'll write less code to solve the same
problems. You'll use better abstractions. You'll write more general code. You
can reuse code--and when you can add features by deleting code, you'll achieve
something great.

X<metaprogramming>
X<code generation>

Writing programs to write programs for you--I<metaprogramming> or I<code
generation>)--offers greater possibilities for abstraction. This can be as
clear as exploiting higher-order programming capabilities or a rat hole down
which you find yourself confused and frightened. The techniques are powerful
and useful. For example, they form the basis of Moose (L<moose>).
generation>)--offers greater possibilities for abstraction. While you can make
a huge mess, you can also build amazing things. For example, metaprogramming
techniques make Moose possible (L<moose>).

The C<AUTOLOAD> technique (L<autoload>) for missing functions and methods
demonstrates this technique in a constrained form; Perl 5's function and method
Expand Down Expand Up @@ -48,18 +47,10 @@ want to) load an optional dependency:
=end programlisting

If C<Monkey::Tracer> is not available, its C<log()> function will exist, but
will do nothing.

=begin sidebar

This isn't necessarily the I<best> way to handle this feature, as the Null
Object pattern offers more encapsulation, but it is I<a> way to do things.

=end sidebar

This simple example is deceptive. You must handle quoting issues to include
variables within your C<eval>d code. Add more complexity to interpolate some
but not others:
will do nothing. Yet this simple example is deceptive. Getting C<eval> right
takes some work; you must handle quoting issues to include variables within
your C<eval>d code. Add more complexity to interpolate some variables but not
others:

=begin programlisting

Expand All @@ -86,12 +77,12 @@ but not others:

=end programlisting

Woe to those who forget a backslash! Good luck convincing your syntax
highlighter what's happening! Worse yet, each invocation of string C<eval>
builds a new data structure representing the entire code. Compiling code isn't
free, either--cheaper than performing IO, perhaps, but not free.
Woe to those who forget a backslash! Good luck convincing your syntax
highlighter what's happening! Worse yet, each invocation of string C<eval>
builds a new data structure representing the entire code. Compiling code isn't
free, either.

Even so, this technique is simple and reasonably easy to understand.
Even with its limitations, this technique is reasonably simple.

=head2 Parametric Closures

Expand Down Expand Up @@ -125,10 +116,9 @@ without requiring additional evaluation:

=end programlisting

This code avoids unpleasant quoting issues and runs more quickly, as there's
only one compilation stage, no matter how many accessors you create. It even
uses less memory by sharing the compiled code between all instances of the
closure. All that differs is the binding to the C<$attrname> lexical. In a
This code avoids unpleasant quoting issues and compiles each closure only once.
It even uses less memory by sharing the compiled code between all closure
instances. All that differs is the binding to the C<$attrname> lexical. In a
long-running process, or with a lot of accessors, this technique can be very
useful.

Expand All @@ -155,41 +145,46 @@ X<typeglobs>

The odd syntax of an asteriskN<Think of it as a I<typeglob sigil>, where a
I<typeglob> is Perl jargon for "symbol table".> deferencing a hash refers to a
symbol in the current I<symbol table>, which is the place in the current
symbol in the current I<symbol table>, which is the portion of the current
namespace which contains globally-accessible symbols such as package globals,
functions, and methods. Assigning a reference to a symbol table entry installs
or replaces the appropriate entry. To promote an anonymous function to a
method, assign that function reference to the appropriate entry in the symbol
table.
functions, and methods. Assigning a reference to a symbol table entry installs
or replaces the appropriate entry. To promote an anonymous function to a
method, store that function's reference in the symbol table.

=begin sidebar

X<CPAN; C<Package::Stash>>
The CPAN module C<Package::Stash> offers a nicer interface to this symbol table
hackery.

=end sidebar

X<C<strict> pragma>
X<pragmas; C<strict>>

This operation refers to a symbol with a string, not a literal variable name,
so it's a symbolic reference and it's necessary to disable C<strict> reference
checking for the operation. Many programs have a subtle bug in similar code,
as they assign and generate in a single line:
Assigning to a symbol table symbol with a string, not a literal variable name,
is a symbolic reference. You must disable C<strict> reference checking for the
operation. Many programs have a subtle bug in similar code, as they assign and
generate in a single line:

=begin programlisting

{
no strict 'refs';

*{ $methname } = sub {
# subtle bug: strict refs
# are disabled in here too
# subtle bug: strict refs disabled in here too
};
}

=end programlisting

This example disables strictures for the outer block as well as the inner
block, the body of the function itself. Only the assignment violates strict
reference checking, so disable strictures for that operation alone.
This example disables strictures for the outer block as well as the body of the
function itself. Only the assignment violates strict reference checking, so
disable strictures for that operation alone.

If the name of the method is a string literal in your source code, rather than
the contents of a variable, you can assign to the relevant symbol directly
rather than through a symbolic reference:
the contents of a variable, you can assign to the relevant symbol directly:

=begin programlisting

Expand All @@ -215,13 +210,13 @@ not be available when you expect it.

X<C<BEGIN>>

Force Perl to run code--to generate other code--during the compilation stage by
wrapping it in a C<BEGIN> block. When the Perl 5 parser encounters a block
labeled C<BEGIN>, it parses the entire block. Provided it contains no syntax
errors, the block will run immediately. When it finishes, parsing will
continue as if there were no interruption.
Force Perl to run code--to generate other code--during compilation by wrapping
it in a C<BEGIN> block. When the Perl 5 parser encounters a block labeled
C<BEGIN>, it parses the entire block. Provided it contains no syntax errors,
the block will run immediately. When it finishes, parsing will continue as if
there had been no interruption.

In practical terms, the difference between writing:
The difference between writing:

=begin programlisting

Expand Down Expand Up @@ -263,18 +258,21 @@ X<modules; implicit C<BEGIN>>

Within a module, any code outside of functions executes when you C<use> it,
because of the implicit C<BEGIN> Perl adds around the C<require> and C<import>
(L<importing>). Any code outside of a function but inside the module will
execute I<before> the C<import()> call occurs. If you C<require> the module,
(L<importing>). Any code outside of a function but inside the module will
execute I<before> the C<import()> call occurs. If you C<require> the module,
there is no implicit C<BEGIN> block. The execution of code outside of
functions will happen at the I<end> of parsing.

Also beware of the interaction between lexical I<declaration> (the association
of a name with a scope) and lexical I<assignment>. The former happens during
compilation, while the latter occurs at the point of execution. This code has
a subtle bug:
Beware of the interaction between lexical I<declaration> (the association of a
name with a scope) and lexical I<assignment>. The former happens during
compilation, while the latter occurs at the point of execution. This code has a
subtle bug:

X<CPAN; C<UNIVERSAL::require>>

=begin programlisting

# adds a require() method to UNIVERSAL
use UNIVERSAL::require;

# buggy; do not use
Expand Down Expand Up @@ -302,11 +300,10 @@ X<objects; meta object protocol>
X<meta object protocol>

Unlike installing function references to populate namespaces and to create
methods, there's no simple default way to create classes in Perl 5.
Fortunately, a mature and powerful distribution is available from the CPAN to
do just this. C<Class::MOP> is the library which makes Moose (L<moose>)
possible. It provides a I<meta object protocol>--a mechanism for creating and
manipulating an object system in terms of itself.
methods, there's no simple way to create classes programmatically in Perl 5.
Moose comes to the rescue, with its bundled C<Class::MOP> library. It provides
a I<meta object protocol>--a mechanism for creating and manipulating an object
system in terms of itself.

Rather than writing your own fragile string C<eval> code or trying to poke into
symbol tables manually, you can manipulate the entities and abstractions of
Expand All @@ -325,7 +322,7 @@ To create a class:
X<metaclass>
X<OO; metaclass>

You can add attributes and methods to this class when you create it:
Add attributes and methods to this class when you create it:

=begin programlisting

Expand Down Expand Up @@ -368,5 +365,8 @@ after you've created it:

=end programlisting

You can similarly create and manipulate and introspect attributes and methods
with C<Class::MOP::Attribute> and C<Class::MOP::Method>.
X<CPAN; C<Class::MOP::Attribute>>
X<CPAN; C<Class::MOP::Method>>

Similarly C<Class::MOP::Attribute> and C<Class::MOP::Method> allow you to
create and manipulate and introspect attributes and methods.
Loading

0 comments on commit 15ee51f

Please sign in to comment.