The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Case - lightweight, pure-Perl, multiway decision constructs

VERSION

This document describes version 1.00 of Case, released June, 2005.

SYNOPSIS

    use Case;
    my $case = dispatcher(
      'mozart'            => sub { "$_ was a Musician!\n"
                                   . Case::action('einstein');
                                 },
      qw(dog cat pig)     => sub { "$_ is an Animal!\n"; },
      qw(einstein newton) => sub { "$_ was a Genius!\n"; },
      'Roy'               => sub { "$_ should fall through..."
                                   . Case::action(Case::default);
                                 },
      Case::default       => sub { "No idea what $_ is.\n" }
    );
    print $case->('mozart');

or

    use Case;
    my $case = dispatcher
        [ qw(dog cat) ]  => sub { print '$_ is an animal';
                                  Case::action(Case::default)
                                },
        [ [1,10] ]       => sub { print 'number in range';
                                  Case::resume('special')
                                },
      special =>
        [ @array, 42 ]   => sub { print 'number in list' },
        [ qr/\w+/    ]   => sub { print 'pattern match' },
        [ \%hash     ]   => sub { print 'entry exists in hash' },
        [ \&sub      ]   => sub { print 'sub returns true value' },
        Case::default    => sub { print 'default' }
    ;
    $case->($val);

DESCRIPTION

The Case module provides two ways to determine which of a set of alternatives a given scalar matches, and take the appropriate action. One is more flexible about what types of matches can be done, the other is more efficient at finding an exact string match.

Setting up the test table

dispatcher examines the argument list to see which flavor of dispatcher you are creating. For exact string matching (only), the arguments should be strings interspersed with coderefs, as in the first example above. For smart-matching, the arguments should be of the form optional-tag/arrayref-of-tests/coderef.

In both cases, the default is preceded by no test (or the equivalent call to Case::default).

Executing a test

In either case, the return value is a coderef. Pass a value to that coderef to execute the code associated with (a test that matches) that value.

Modifying the dispatch table

The string-matching dispatcher allows you to modify the table by passing more than one argument to the dispatcher you have created. You can define new entries using the same syntax as when originally defining it, and you can delete entries by passing undef as the action coderef associated with the terms you want deleted. Passing a single coderef will redefine the default action.

You cannot adjust the smart-matching dispatcher after it has been created.

FALLTHROUGH, CHAINING, and RESUME

In C's switch statement, fallthrough exists for you to be able to stack terms up. You get that behavior automatically here by preceding a coderef with all the terms that map to it. Fallthrough from one coderef to the next is discouraged in C, and does not happen in this implementation, per se.

Within the action coderefs, you can call the coderef associated with another term or test by calling the magically-defined Case::action( ) with the term (in the case of string-matching flavor), or the tag (in the case of smart-matching flavor) of the test. This is a safer and more flexible way to chain actions together than C-style fallthrough. To call the default case, you can pass no arguments, or (equivalently, but self-documenting), Case::default( ) as the argument.

If you wish to resume testing in the smart-matching flavor, call Case::resume(tag). Calling resume with no argument will cause it to fall through to the next test (which need not have a tag).

Note: The chaining calls are subroutine calls, and will return to the caller just like any subroutine. If the chaining call is not the last statement in your action, you should probably make it part of a return statement.

SMART-MATCHING RULES

A match is found when an element of the arrayref, evaluated according to this table, yields a true value:

 Element ($test)    Example         Operation
 ================   =============   =====================================

 Regex              qr/foo|bar/     /$test/

 Number             3.14            $_ == $test

 coderef            sub { /foo/ }   $test->($_)

 number range       [100,1000]      $test->[0] <= $_ and $_ <= $test->[1]

 text range         ['a', 'arf']    $test->[0] le $_ and $_ le $test->[1]

 hashref            \%hash          exists $test->{$_}

 any other scalar   'a string'      $_ eq $test

Ranges

An arrayref is interpreted as a range of either numbers or strings. The test will pass if the value being checked is between the two range endpoints (inclusive).

The endpoints can appear in either order, but it is an error to have one numeric and one non-numeric, a ref, or not exactly two values in the range specifier.

To smart-match against all elements of an array, you can include a ref to the array as a testset (don't wrap it in [] ). To combine it with other tests, make the action sub {Case::resume()} and put the rest of the tests as the next testset.

Coderef Tests

Coderef tests are your fallback option. If the test you want to perform is not one of the provided variety, write it yourself and plug it in.

Coderefs (both tests and actions) are called with the value being tested aliased to both $_ and (the first element of) @_. That is so you can compactly do things like sub {s/foo/bar} and also sub {grep /$_[0]/, @foo}.

Standard Exports

The Case module exports dispatcher by default

Optional Exports

The following routines will be exported into your namespace if you specifically ask that they be imported:

default is a completely empty subroutine, useful as a tag to label the default case (you don't need to import it; Case::default => \&whatever looks fine, but you may import it if you want).

is_number is a helper function for distinguishing between numbers and strings.

NOTES

When smart-matching ordinary scalars, if both are determined to be numbers, then numeric comparison is done; otherwise, string comparison is done. Stringify or numify as necessary to get the comparison you want.

This is not an object-oriented module. Nothing is blessed into the Case package.

AUTHOR

Roy Johnson