Source

perl-begin / lib / tutorials / modern-perl / xhtml / chapter_08.html

Full commit
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="generator" content="HTML Tidy for Linux (vers 25 March 2009), see www.w3.org" />
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="../styles/style.css" type="text/css" />
</head>
<body>
<h1 id="heading_id_2">Style and Efficacy</h1>
<p>Quality matters.</p>
<p>Programs have bugs. Programs need maintenance and expansion. Programs have multiple programmers.</p>
<p>Programming well requires us to find a balance between getting the job done and allowing us to do so well into the future. We can trade any of time, resources, and quality for any other. How well we do that determines our skill as pragmatic craftworkers.</p>
<p>Understanding Perl is important. So is cultivating a sense of good taste. The only way to do so is to practice maintaining code and reading and writing great code. This path has no shortcuts, but this path does have guideposts.</p>
<div id="style"></div>
<h2 id="heading_id_3">Writing Maintainable Perl</h2>
<div id="imaintainability_0"></div>
<p><em>Maintainability</em> is the nebulous measurement of the ease of understanding and modifying an existing program. Set aside some code for six months, then return to it anew. Maintainability measures the difficulty you face making changes.</p>
<p>Maintainability is neither a syntactic concern nor a measurement of how a non-programmer might view your code. Assume a competent programmer who understands the nature of the problem the code must solve. What problems get in the way of fixing a bug or adding an enhancement correctly?</p>
<p>The ability to write maintainable code comes from hard-won experience and comfort with idioms and techniques and the dominant style of the language. Yet even novices can improve the maintainability of their code by adhering to a few principles:</p>
<ul>
<li><em>Remove duplication.</em> Bugs lurk in sections of repeated and similar code--when you fix a bug on one section, did you fix it in others? When you update one section, did you update the others?</li>
<li style="list-style: none; display: inline">
<p>Well-designed systems have little duplication. They use functions, modules, objects, and roles to extract duplicate code into reusable components which accurately model the domain of the problem. The best designs allow you to add features by <em>removing</em> code.</p>
</li>
<li><em>Name entities well.</em> Your code tells a story. Every named symbol--variables, functions, models, classes--allows you to clarify or obfuscate your intent. The ease of choosing names reveals your understanding of the problem and your design. Choose your names carefully.</li>
<li><em>Avoid unnecessary cleverness.</em> Concise code is good, when it reveals the intention of the code. Clever code hides your intent behind flashy tricks. Perl allows you to write the right code at the right time. Where possible, choose the most obvious solution. Experience, good taste, and knowing what really matters will guide you.</li>
<li style="list-style: none; display: inline">
<p>Some problems require clever solutions. Encapsulate this code behind a simple interface and document your cleverness.</p>
</li>
<li><em>Embrace simplicity.</em> All else being equal, a simpler program is easier to maintain than its more complex workalike. Simplicity means knowing what's most important and doing just that.</li>
<li style="list-style: none; display: inline">
<p>This is no excuse to avoid error checking or modularity or validation or security. Simple code can use advanced features. Simple code can use great piles of CPAN modules. Simple code may require work to understand. Yet simple code solves problems effectively, without unnecessary work.</p>
<p>Sometimes you need powerful, robust code. Sometimes you need a one-liner. Simplicity means knowing the difference and building only what you need.</p>
</li>
</ul>
<h2 id="heading_id_4">Writing Idiomatic Perl</h2>
<div id="iidioms_0"></div>
<p>Perl borrows liberally from other languages. Perl lets you write the code you want to write. C programmers often write C-style Perl, just as Java programmers write Java-style Perl. Effective Perl programmers write Perlish Perl, embracing the language's idioms.</p>
<ul>
<li><em>Understand community wisdom.</em> Perl programmers often host fierce debates over techniques. Perl programmers also often share their work, and not just on the CPAN. Pay attention, and gain enlightenment on the tradeoffs between various ideals and styles.</li>
<li style="list-style: none; display: inline">
<p>CPAN developers, Perl Mongers, and mailing list participants have hard-won experience solving problems in myriad ways. Talk to them. Read their code. Ask questions. Learn from them and let them learn from you.</p>
<div id="iCPAN__iPerl5858Critic_0"></div>
<div id="iCPAN__iPerl5858Tidy_0"></div>
<div id="iCPAN__iCPAN5858Mini_0"></div>
</li>
<li><em>Follow community norms.</em> Perl is a community of toolsmiths. We solve broad problems, including static code analysis (<code>Perl::Critic</code>), reformatting (<code>Perl::Tidy</code>), and private distribution systems (<code>CPAN::Mini</code>). Take advantage of the CPAN infrastructure; follow the CPAN model of writing, documenting, packaging, testing, and distributing your code.</li>
<li><em>Read code.</em> Join a mailing list such as Perl Beginners (<span class="url">http://learn.perl.org/faq/beginners.html</span>), browse PerlMonks (<span class="url">http://perlmonks.org/</span>), and otherwise immerse yourself in the community <span class="footnote">(footnote: See <span class="url">http://www.perl.org/community.html</span>.)</span>. Read code and try to answer questions--even if you never post them, this is a great opportunity to learn.</li>
</ul>
<h2 id="heading_id_5">Writing Effective Perl</h2>
<div id="iefficacy_0"></div>
<p>Maintainability is ultimately a design concern. Good design comes from practicing good habits:</p>
<ul>
<li><em>Write testable code.</em> Writing an effective test suite exercises the same design skills as writing effective code. Code is code. Good tests also give you the confidence to modify a program while keeping it running correctly.</li>
<li><em>Modularize.</em> Enforce encapsulation and abstraction boundaries. Find the right interfaces between components. Name things well and put them where they belong. Modularity forces you to reason about the abstractions in your programs to understand how everything fits together. Find the pieces that don't fit well. Improve your code until they do.</li>
<li><em>Follow sensible coding standards.</em> Effective guidelines govern error handling, security, encapsulation, API design, project layout, and other maintainability concerns. Excellent guidelines help developers communicate with each other with code. You solve problems. Speak clearly.</li>
<li><em>Exploit the CPAN.</em> Perl programmers solve problems. Then we share those solutions. Take advantage of this force multiplier. Search the CPAN first for a solution or partial solution to your problem. Invest your research time; it will pay off.</li>
<li style="list-style: none; display: inline">
<p>If you find a bug, report it. Patch it, if possible. Fix a typo. Ask for a feature. Say "Thank you!" We are better together than we are separately. We are powerful and effective when we reuse code.</p>
<p>When you're ready, when you solve a new problem, share it. Join us. We solve problems.</p>
</li>
</ul>
<h2 id="heading_id_6">Exceptions</h2>
<div id="exceptions"></div>
<div id="iexceptions_0"></div>
<p>Programming well means anticipating the unexpected. Files that should exist don't. That huge disk that will never fill up does. The always-on network isn't. The unbreakable database breaks. Exceptions happen, and robust software must handle them. If you can recover, great! If you can't, log the relevant information and retry.</p>
<p>Perl 5 handles exceptional conditions through <em>exceptions</em>: a dynamically-scoped control flow mechanism designed to raise and handle errors.</p>
<h3 id="heading_id_7">Throwing Exceptions</h3>
<div id="throwing_exceptions"></div>
<p>Suppose you want to write a log file. If you can't open the file, something has gone wrong. Use <code>die</code> to throw an exception:</p>
<div class="programlisting">
<pre>
<code>    sub open_log_file
    {
        my $name = shift;
        open my $fh, '&gt;&gt;', $name
            <strong>or die "Can't open logging file '$name': $!";</strong>
        return $fh;
    }</code>
</pre></div>
<div id="ibuiltins__idie_0"></div>
<div id="iexceptions__ithrowing_0"></div>
<div id="iexceptions__ithrowing_strings_0"></div>
<div id="iexceptions__idie_0"></div>
<div id="i3664_1"></div>
<div id="iexceptions__i3664_0"></div>
<p><code>die()</code> sets the global variable <code>$@</code> to its operand and immediately exits the current function <em>without returning anything</em>. This thrown exception will continue up the call stack (<a href="chapter_10.html#controlled_execution">Controlled Execution</a>(controlled_execution)) until something catches it. If nothing catches the exception, the program will exit with an error.</p>
<p>Exception handling uses the same dynamic scope (<a href="chapter_05.html#dynamic_scope">Dynamic Scope</a>(dynamic_scope)) as <code>local</code> symbols.</p>
<h3 id="heading_id_8">Catching Exceptions</h3>
<div id="catching_exceptions"></div>
<div id="iexceptions__icatching_0"></div>
<p>Sometimes an exception exiting the program is useful. A program run as a timed process might throw an exception when the error logs have filled, causing an SMS to go out to administrators. Yet not all exceptions should be fatal. Good programs can recover from some, or at least save their state and exit cleanly.</p>
<div id="ibuiltins__ieval_0"></div>
<div id="ieval__iblock_0"></div>
<p>Use the block form of the <code>eval</code> operator to catch an exception:</p>
<div class="programlisting">
<pre>
<code>    # log file may not open
    my $fh = eval { open_log_file( 'monkeytown.log' ) };</code>
</pre></div>
<p>If the file open succeeds, <code>$fh</code> will contain the filehandle. If it fails, <code>$fh</code> will remain undefined, and program flow will continue.</p>
<p>The block argument to <code>eval</code> introduces a new scope, both lexical and dynamic. If <code>open_log_file()</code> called other functions and something eventually threw an exception, this <code>eval</code> could catch it.</p>
<div id="imagic_variables__i3664_1"></div>
<p>An exception handler is a blunt tool. It will catch all exceptions in its dynamic scope. To check which exception you've caught (or if you've caught an exception at all), check the value of <code>$@</code>. Be sure to <code>local</code>ize <code>$@</code> before you attempt to catch an exception; remember that <code>$@</code> is a global variable:</p>
<div class="programlisting">
<pre>
<code>    <strong>local $@;</strong>

    # log file may not open
    my $fh = eval { open_log_file( 'monkeytown.log' ) };

    # caught exception
    <strong>if (my $exception = $@) { ... }</strong></code>
</pre></div>
<div id="iexceptions__irethrowing_0"></div>
<p>Copy <code>$@</code> to a lexical variable immediately to avoid the possibility of subsequent code clobbering the global variable <code>$@</code>. You never know what else has used an <code>eval</code> block elsewhere and reset <code>$@</code>.</p>
<p><code>$@</code> usually contains a string describing the exception. Inspect its contents to see whether you can handle the exception:</p>
<div class="programlisting">
<pre>
<code>    if (my $exception = $@)
    {
        die $exception
            unless $exception =~ /^Can't open logging/;
        $fh = log_to_syslog();
    }</code>
</pre></div>
<p>Rethrow an exception by calling <code>die()</code> again. Pass the existing exception or a new one as necessary.</p>
<div id="iexceptions__ithrowing_objects_0"></div>
<p>Applying regular expressions to string exceptions can be fragile, because error messages may change over time. This includes the core exceptions that Perl itself throws. Fortunately, you may also provide a reference--even a blessed reference--to <code>die</code>. This allows you to provide much more information in your exception: line numbers, files, and other debugging information. Retrieving this information from something structured is much easier than parsing it out of a string. Catch these exceptions as you would any other exception.</p>
<div id="iexceptions__icustom_classes_with_Exception5858Class_0"></div>
<div id="iException5858Class_0"></div>
<p>The CPAN distribution <code>Exception::Class</code> makes creating and using exception objects easy:</p>
<div class="programlisting">
<pre>
<code>    package Zoo::Exceptions
    {
        use Exception::Class
            'Zoo::AnimalEscaped',
            'Zoo::HandlerEscaped';
    }

    sub cage_open
    {
        my $self = shift;
        Zoo::AnimalEscaped-&gt;throw
            unless $self-&gt;contains_animal;
        ...
    }

    sub breakroom_open
    {
        my $self = shift;
        Zoo::HandlerEscaped-&gt;throw
            unless $self-&gt;contains_handler;
        ...
    }</code>
</pre></div>
<h3 id="heading_id_9">Exception Caveats</h3>
<div id="exception_caveats"></div>
<div id="iexceptions__icaveats_0"></div>
<p>Though throwing exceptions is relatively simple, catching them is less so. Using <code>$@</code> correctly requires you to navigate several subtle risks:</p>
<ul>
<li>Un<code>local</code>ized uses further down the dynamic scope may modify <code>$@</code></li>
<li>It may contain an object which overrides its boolean value to return false</li>
<li>A signal handler (especially the <code>DIE</code> signal handler) may change <code>$@</code></li>
<li>The destruction of an object during scope exit may call <code>eval</code> and change <code>$@</code></li>
</ul>
<div id="iexceptions__ihandling_safely_with_Try5858Tiny_0"></div>
<div id="iTry5858Tiny_0"></div>
<p>Perl 5.14 fixed some of these issues. Granted, they occur very rarely, but they're often difficult to diagnose and to fix. The <code>Try::Tiny</code> CPAN distribution improves the safety of exception handling <em>and</em> the syntax <span class="footnote">(footnote: In fact, <code>Try::Tiny</code> helped inspire improvements to Perl 5.14's exception handling.)</span>.</p>
<p><code>Try::Tiny</code> is easy to use:</p>
<div class="programlisting">
<pre>
<code>    use Try::Tiny;

    my $fh = try   { open_log_file( 'monkeytown.log' ) }
             catch { log_exception( $_ ) };</code>
</pre></div>
<p><code>try</code> replaces <code>eval</code>. The optional <code>catch</code> block executes only when <code>try</code> catches an exception. <code>catch</code> receives the caught exception as the topic variable <code>$_</code>.</p>
<h3 id="heading_id_10">Built-in Exceptions</h3>
<div id="builtin_exceptions"></div>
<div id="iexceptions__icore_0"></div>
<p>Perl 5 itself throws several exceptional conditions. <code>perldoc perldiag</code> lists several "trappable fatal errors". While some are syntax errors thrown during the compilation process, you can catch the others during runtime. The most interesting are:</p>
<ul>
<li>Using a disallowed key in a locked hash (<a href="chapter_03.html#locked_hashes">Locking Hashes</a>(locked_hashes))</li>
<li>Blessing a non-reference (<a href="chapter_07.html#blessed_references">Blessed References</a>(blessed_references))</li>
<li>Calling a method on an invalid invocant (<a href="chapter_07.html#moose">Moose</a>(moose))</li>
<li>Failing to find a method of the given name on the invocant</li>
<li>Using a tainted value in an unsafe fashion (<a href="chapter_09.html#taint">Taint</a>(taint))</li>
<li>Modifying a read-only value</li>
<li>Performing an invalid operation on a reference (<a href="chapter_03.html#references">References</a>(references))</li>
</ul>
<div id="iautodie_0"></div>
<p>Of course you can also catch exceptions produced by <code>autodie</code> (<a href="chapter_12.html#autodie">The autodie Pragma</a>(autodie)) and any lexical warnings promoted to exceptions (<a href="chapter_09.html#registering_warnings">Registering Your Own Warnings</a>(registering_warnings)).</p>
<h2 id="heading_id_11">Pragmas</h2>
<div id="pragmas"></div>
<div id="ipragmas_0"></div>
<div id="imodules__ipragmas_0"></div>
<p>Most Perl 5 extensions are modules which provide new functions or define classes (<a href="chapter_07.html#moose">Moose</a>(moose)). Some modules instead influence the behavior of the language itself, such as <code>strict</code> or <code>warnings</code>. Such a module is a <em>pragma</em>. By convention, pragmas have lower-case names to differentiate them from other modules.</p>
<h3 id="heading_id_12">Pragmas and Scope</h3>
<div id="ipragmas__iscope_0"></div>
<p>Pragmas work by exporting specific behavior or information into the lexical scopes of their callers. Just as declaring a lexical variable makes a symbol name available within a scope, using a pragma makes its behavior effective within that scope:</p>
<div class="programlisting">
<pre>
<code>    {
        # $lexical <strong>not</strong> visible; strict <strong>not</strong> in effect
        {
            use strict;
            my $lexical = 'available here';
            # $lexical <strong>is</strong> visible; strict <strong>is</strong> in effect
            ...
        }
        # $lexical again invisible; strict <strong>not</strong> in effect
    }</code>
</pre></div>
<p>Just as lexical declarations affect inner scopes, pragmas maintain their effects within inner scopes:</p>
<div class="programlisting">
<pre>
<code>    # file scope
    use strict;

    {
        # inner scope, but strict still in effect
        my $inner = 'another lexical';
        ...
    }</code>
</pre></div>
<h3 id="heading_id_13">Using Pragmas</h3>
<div id="ipragmas__ienabling_0"></div>
<p><code>use</code> a pragma as you would any other module. Pragmas take arguments, such as a minimum version number to use and a list of arguments to change the pragma's behavior:</p>
<div class="programlisting">
<pre>
<code>    # require variable declarations, prohibit barewords
    use strict qw( subs vars );</code>
</pre></div>
<div id="ipragmas__idisabling_0"></div>
<div id="ibuiltins__ino_0"></div>
<p>Sometimes you need to <em>disable</em> all or part of those effects within a further nested lexical scope. The <code>no</code> builtin performs an unimport (<a href="chapter_05.html#importing">Importing</a>(importing)), which undoes the effects of well-behaved pragmas. For example, to disable the protection of <code>strict</code> when you need to do something symbolic:</p>
<div class="programlisting">
<pre>
<code>    use Modern::Perl;
    # or use strict;

    {
        no strict 'refs';
        # manipulate the symbol table here
    }</code>
</pre></div>
<h3 id="heading_id_14">Useful Pragmas</h3>
<div id="imagic_variables__i3694H_0"></div>
<div id="ipragmas__iwriting_0"></div>
<p>Perl 5.10.0 added the ability to write your own lexical pragmas in pure Perl code. <code>perldoc perlpragma</code> explains how to do so, while the explanation of <code>$^H</code> in <code>perldoc perlvar</code> explains how the feature works.</p>
<div id="ipragmas__iuseful_core_pragmas_0"></div>
<p>Even before 5.10, Perl 5 included several useful core pragmas.</p>
<div id="ipragmas__istrict_0"></div>
<ul>
<li>the <code>strict</code> pragma enables compiler checking of symbolic references, bareword use, and variable declaration.</li>
<li style="list-style: none; display: inline">
<div id="ipragmas__iwarnings_0"></div>
</li>
<li>the <code>warnings</code> pragma enables optional warnings for deprecated, unintended, and awkward behaviors.</li>
<li style="list-style: none; display: inline">
<div id="ipragmas__iutf8_1"></div>
</li>
<li>the <code>utf8</code> pragma forces the parser to interpret the source code with UTF-8 encoding.</li>
<li style="list-style: none; display: inline">
<div id="ipragmas__iautodie_0"></div>
</li>
<li>the <code>autodie</code> pragma enables automatic error checking of system calls and builtins.</li>
<li style="list-style: none; display: inline">
<div id="ipragmas__iconstant_0"></div>
</li>
<li>the <code>constant</code> pragma allows you to create compile-time constant values (see the CPAN's <code>Const::Fast</code> for an alternative).</li>
<li style="list-style: none; display: inline">
<div id="ipragmas__ivars_0"></div>
</li>
<li>the <code>vars</code> pragma allows you to declare package global variables, such as <code>$VERSION</code> or <code>@ISA</code> (<a href="chapter_07.html#blessed_references">Blessed References</a>(blessed_references)).</li>
<li style="list-style: none; display: inline">
<div id="ipragmas__ifeature_1"></div>
</li>
<li>the <code>feature</code> pragma allows you to enable and disable post-5.10 features of Perl 5 individually. Where <code>use 5.14;</code> enables all of the Perl 5.14 features and the <code>strict</code> pragma, <code>use feature ':5.14';</code> does the same. This pragma is more useful to <em>disable</em> individual features in a lexical scope.</li>
<li style="list-style: none; display: inline">
<div id="ipragmas__iless_0"></div>
</li>
<li>the <code>less</code> pragma demonstrates how to write a pragma.</li>
</ul>
<p>The CPAN has begun to gather non-core pragmas:</p>
<div id="iCPAN__iautobox_0"></div>
<div id="iCPAN__iperl5i_0"></div>
<div id="iCPAN__iautovivification_0"></div>
<div id="iCPAN__iindirect_0"></div>
<ul>
<li><code>autobox</code> enables object-like behavior for Perl 5's core types (scalars, references, arrays, and hashes).</li>
<li><code>perl5i</code> combines and enables many experimental language extensions into a coherent whole.</li>
<li><code>autovivification</code> disables autovivification (<a href="chapter_03.html#autovivification">Autovivification</a>(autovivification))</li>
<li><code>indirect</code> prevents the use of indirect invocation (<a href="chapter_11.html#indirect_objects">Indirect Objects</a>(indirect_objects))</li>
</ul>
<p>These tools are not widely used yet. The latter two can help you write more correct code, while the former two are worth experimenting with in small projects. They represent what Perl 5 might be.</p>
</body>
</html>