Contex...Return(3pm)

๐Ÿ“ฆ
version 0.004014 ยท 7 versions
, in debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

okay bestie, welcome to the Contextual::Return (3pm) manual โ€” and no, it's not about returning your ex's texts, it's about returning different values depending on context ๐Ÿ’…โœจ

โœจ NAME โœจ

Contextual::Return โ€” Create context-sensitive return values. Like, your sub can read the room and return exactly what's needed, periodt. ๐ŸŽ€

๐ŸŒธ VERSION ๐ŸŒธ

This document describes Contextual::Return version 0.004014 โ€” that's the exact number, bestie, don't @ me. ๐Ÿ’–

โœจ SYNOPSIS โœจ

okay so here's the whole vibe. You can return a bunch of labeled blocks and Perl picks the right one for the context. It's giving choose-your-own-adventure but for return values:

           use Contextual::Return;
           use Carp;

           sub foo {
               return
                   SCALAR { 'thirty-twelve' }
                   LIST   { 1,2,3 }

                   BOOL { 1 }
                   NUM  { 7*6 }
                   STR  { 'forty-two' }

                   HASHREF  { {name => 'foo', value => 99} }
                   ARRAYREF { [3,2,1] }

                   GLOBREF  { \*STDOUT }
                   CODEREF  { croak "Don't use this result as code!"; }
               ;
           }

           # and later...

           if (my $foo = foo()) {
               for my $count (1..$foo) {
                   print "$count: $foo is:\n"
                       . "  array: @{$foo}\n"
                       . "  hash:  $foo->{name} => $foo->{value}\n"
                       ;
               }
               print {$foo} $foo->();
           }

๐Ÿ’œ DESCRIPTION ๐Ÿ’œ

okay so usually when you need a sub to return different things in list, scalar, or void context, you end up writing something like this hot mess:

           sub get_server_status {
               my ($server_ID) = @_;

               # Acquire server data somehow...
               my %server_data = _ascertain_server_status($server_ID);

               # Return different components of that data,
               # depending on call context...
               if (wantarray()) {
                   return @server_data{ qw(name uptime load users) };
               }
               if (defined wantarray()) {
                   return $server_data{load};
               }
               if (!defined wantarray()) {
                   carp 'Useless use of get_server_status() in void context';
                   return;
               }
               else {
                   croak q{Bad context! No biscuit!};
               }
           }

That works, but honestly? The code could be way more readable. In its simplest usage, this module makes that code more readable by providing three subroutines โ€” LIST(), SCALAR(), and VOID() โ€” that are true only when the current subroutine is called in the corresponding context:

           use Contextual::Return;

           sub get_server_status {
               my ($server_ID) = @_;

               # Acquire server data somehow...
               my %server_data = _ascertain_server_status($server_ID);

               # Return different components of that data
               # depending on call context...
               if (LIST)   { return @server_data{ qw(name uptime load users) } }
               if (SCALAR) { return $server_data{load}                         }
               if (VOID)   { print "$server_data{load}\n"                      }
               else        { croak q{Bad context! No biscuit!}                 }
           }

โœจ Contextual returns โœจ

Those three subroutines can also be used in another way: as labels on a series of contextual return blocks โ€” collectively known as a contextual return sequence. When a context sequence is returned, it automatically selects the appropriate contextual return block for the calling context. So the previous example could be written even more cleanly as:

           use Contextual::Return;

           sub get_server_status {
               my ($server_ID) = @_;

               # Acquire server data somehow...
               my %server_data = _ascertain_server_status($server_ID);

               # Return different components of that data
               # depending on call context...
               return (
                   LIST    { return @server_data{ qw(name uptime load users) } }
                   SCALAR  { return $server_data{load}                         }
                   VOID    { print "$server_data{load}\n"                      }
                   DEFAULT { croak q{Bad context! No biscuit!}                 }
               );
           }

The context sequence automatically selects the appropriate block for each call context. Like, it just knows. ๐Ÿ”ฎ

๐ŸŒˆ Lazy contextual return values ๐ŸŒˆ

LIST and VOID blocks are always executed during the return statement. But scalar return blocks โ€” SCALAR, STR, NUM, BOOL, etc. โ€” are not. Instead, returning any of those scalar block types causes the subroutine to return an object that lazily evaluates that block only when the return value is actually used. So it's giving "I'll do it later" energy, and honestly same. ๐Ÿ˜ญ

This means returning a SCALAR block is a super convenient way to implement a subroutine with a lazy return value. For example:

           sub digest {
               return SCALAR {
                   my ($text) = @_;
                   md5($text);
               }
           }

           my $digest = digest($text);

           print $digest;   # md5() called only when $digest used as string

To better document this usage, the SCALAR block has a synonym: LAZY.

           sub digest {
               return LAZY {
                   my ($text) = @_;
                   md5($text);
               }
           }

๐Ÿ”ฅ Active contextual return values ๐Ÿ”ฅ

Once a return value has been lazily evaluated in a given context, the resulting value is cached, and thereafter reused in that same context. So it's like, once it does the thing, it remembers. ๐Ÿ’…

But wait โ€” you can specify that, rather than being cached, the value should be re-evaluated every time the value is used. That's ACTIVE:

            sub make_counter {
               my $counter = 0;
               return ACTIVE
                   SCALAR   { ++$counter }
                   ARRAYREF { [1..$counter] }
           }

           my $idx = make_counter();

           print "$idx\n";      # 1
           print "$idx\n";      # 2
           print "[@$idx]\n";   # [1 2]
           print "$idx\n";      # 3
           print "[@$idx]\n";   # [1 2 3]

๐Ÿฆ‹ Semi-lazy contextual return values ๐Ÿฆ‹

Sometimes, single or repeated lazy evaluation of a scalar return value in different contexts isn't what you really want. Sometimes what you really want is for the return value to be lazily evaluated once only โ€” the first time it's used in any context โ€” and then for that first value to be reused whenever the return value is subsequently reevaluated in any other context.

To get that behaviour, you can use the FIXED modifier, which causes the return value to morph itself into the actual value the first time it is used. Like it commits and then never changes again. For example:

           sub lazy {
               return
                   SCALAR { 42 }
                   ARRAYREF { [ 1, 2, 3 ] }
               ;
           }

           my $lazy = lazy();
           print $lazy + 1;            # 43
           print "@{$lazy}";           # 1 2 3

           sub semilazy {
               return FIXED
                   SCALAR { 42 }
                   ARRAYREF { [ 1, 2, 3 ] }
               ;
           }

           my $semi = semilazy();
           print $semi + 1;            # 43
           print "@{$semi}";           # die q{Can't use string ("42") as an ARRAY ref}

๐Ÿ’– Finer distinctions of scalar context ๐Ÿ’–

Because the scalar values returned from a context sequence are lazily evaluated, it becomes possible to be more specific about what kind of scalar value should be returned: a boolean, a number, or a string. To support those distinctions, Contextual::Return provides four extra context blocks: NUM, STR, BOOL, and PUREBOOL:

           sub get_server_status {
               my ($server_ID) = @_;

               # Acquire server data somehow...
               my %server_data = _ascertain_server_status($server_ID);

               # Return different components of that data
               # depending on call context...
               return (
                      LIST { @server_data{ qw(name uptime load users) }          }
                  PUREBOOL { $_ = $server_data{uptime}; $server_data{uptime} > 0 }
                      BOOL { $server_data{uptime} > 0                            }
                      NUM  { $server_data{load}                                  }
                      STR  { "$server_data{name}: $server_data{uptime}"          }
                      VOID { print "$server_data{load}\n"                        }
                   DEFAULT { croak q{Bad context! No biscuit!}                   }
               );
           }

With these in place, the object returned from a scalar-context call to get_server_status() now behaves differently, depending on how it's used. For example:

           if ( my $status = get_server_status() ) {  # BOOL: True if uptime > 0
               $load_distribution[$status]++;         # INT:  Evaluates to load value
               print "$status\n";                     # STR:  Prints "name: uptime"
           }

           if (get_server_status()) {                 # PUREBOOL: also sets $_;
               print;                                 # ...which is then used here
           }

โœจ Boolean vs Pure Boolean contexts โœจ

There is a special subset of boolean contexts where the return value is being used and immediately thrown away. For example, in the loop:

           while (get_data()) {
               ...
           }

the value returned by get_data() is tested for truth and then discarded. This is known as pure boolean context. In contrast, in the loop:

           while (my $data = get_data()) {
               ...
           }

the value returned by get_data() is first assigned to $data, then tested for truth. Because of the assignment, the return value is not discarded after the boolean test. This is ordinary boolean context.

In Perl, pure boolean context is often associated with a special side-effect, that does not occur in regular boolean contexts. For example:

           while (<>) {...}         # $_ set as side-effect of pure boolean context

           while ($v = <>) {...}    # $_ NOT set in ordinary boolean context

Contextual::Return supports this with a special subcase of BOOL named <PUREBOOL>. In pure boolean contexts, Contextual::Return will call a PUREBOOL handler if one has been defined, or fall back to a BOOL or SCALAR handler if no PUREBOOL handler exists. In ordinary boolean contexts only the BOOL or SCALAR handlers are tried, even if a PUREBOOL handler is also defined.

Typically PUREBOOL handlers are set up to have some side-effect (most commonly: setting $_ or <$@>), like so:

           sub get_data {
               my ($succeeded, @data) = _go_and_get_data();

               return
                   PUREBOOL { $_ = $data[0]; $succeeded; }
                       BOOL {                $succeeded; }
                     SCALAR {                $data[0];   }
                       LIST {                @data;      }
           }

However, there is no requirement that they have side-effects. For example, they can also be used to implement "look-but-don't-retrieve-yet" checking:

           sub get_data {
               my $data;
               return
                   PUREBOOL {        _check_for_but_dont_get_data();   }
                       BOOL { defined( $data ||= _go_and_get_data() ); }
                        REF {          $data ||= _go_and_get_data();   }
           }

๐ŸŒŸ Self-reference within handlers ๐ŸŒŸ

Any handler can refer to the contextual return object it is part of, by calling the RETOBJ() function. This is particularly useful for PUREBOOL and LIST handlers. For example:

    return
        PUREBOOL { $_ = RETOBJ; next handler; }
            BOOL { !$failed;                  }
         DEFAULT { $data;                     };

๐ŸŒˆ Referential contexts ๐ŸŒˆ

okay so bestie, the other huge flavor of scalar return value is a reference. and Contextual::Return has a whole lil squad of return blocks that let you say exactly what gets handed back โ€” lazily, of course, we don't do eager around here ๐Ÿ’… โ€” depending on whether your sub's return value is being used as:

  • a reference to a scalar โ†’ SCALARREF {...}
  • a reference to an array โ†’ ARRAYREF {...}
  • a reference to a hash โ†’ HASHREF {...}
  • a reference to a subroutine โ†’ CODEREF {...}
  • a reference to a typeglob โ†’ GLOBREF {...}

so like, remember that server status sub from before? โœจ we can glow it up so it also returns a hash reference, which is how you get what the docs call "named return values". it's giving API design, honestly:

    sub get_server_status {
        my ($server_ID) = @_;

        # Acquire server data somehow...
        my %server_data = _ascertain_server_status($server_ID);

        # Return different components of that data
        # depending on call context...
        return (
               LIST { @server_data{ qw(name uptime load users) }  }
               BOOL { $server_data{uptime} > 0                    }
                NUM { $server_data{load}                          }
                STR { "$server_data{name}: $server_data{uptime}"  }
               VOID { print "$server_data{load}\n"                }
            HASHREF { return \%server_data                        }
            DEFAULT { croak q{Bad context! No biscuit!}           }
        );
    }

    # and later...

    my $users = get_server_status->{users};

    # or, lazily...

    my $server = get_server_status();

    print "$server->{name} load = $server->{load}\n";

๐Ÿฆ‹ Interpolative referential contexts ๐Ÿฆ‹

the SCALARREF {...} and ARRAYREF {...} blocks are lowkey iconic for when you need to interpolate a subroutine straight into a string. like, say you've got a sub doing this:

    sub get_todo_tasks {
        return (
            SCALAR { scalar @todo_list }      # How many?
            LIST   { @todo_list        }      # What are they?
        );
    }

    # and later...

    print "There are ", scalar(get_todo_tasks()), " tasks:\n",
            get_todo_tasks();

then you could make interpolating calls to that sub so much easier by just adding:

    sub get_todo_tasks {
        return (
            SCALAR { scalar @todo_list }      # How many?
            LIST   { @todo_list        }      # What are they?

            SCALARREF { \scalar @todo_list }  # Ref to how many
            ARRAYREF  { \@todo_list        }  # Ref to them
        );
    }

    # and then...

    print "There are ${get_todo_tasks()} tasks:\n@{get_todo_tasks()}";

and ngl? this behaviour is so useful that it's literally the default. ๐Ÿซถ if you don't hand over an explicit SCALARREF {...} block, Contextual::Return just quietly supplies an implicit one for you โ€” it returns a reference to whatever would've been returned in scalar context. same deal with ARRAYREF {...}: no block specified, module gives you one that takes the list-context return value and wraps it up in an array reference. automatic. slay.

so you could genuinely just write:

    sub get_todo_tasks {
        return (
            SCALAR { scalar @todo_list }      # How many?
            LIST   { @todo_list        }      # What are they?
        );
    }

    # and still do this...

    print "There are ${get_todo_tasks()} tasks:\n@{get_todo_tasks()}";

and it works. no cap. ๐Ÿ’–


๐ŸŒธ Fallback contexts ๐ŸŒธ

so as the last few sections have been hinting at, BOOL {...}, NUM {...}, STR {...} and all those *REF {...} blocks are basically special cases of the general SCALAR {...} context block. so if your sub gets called in one of those specialized contexts but you didn't bother writing the matching block, the more general SCALAR {...} block steps up instead โ€” that is, if you specified one. team player energy.

like, for example:

    sub read_value_from {
        my ($fh) = @_;

        my $value = <$fh>;
        chomp $value;

        return (
            BOOL   { defined $value }
            SCALAR { $value         }
        );
    }

this makes sure read_value_from() returns true in boolean contexts if the read went fine โœ…. but because no specific NUM {...} or STR {...} return behaviours were given, the sub falls back on its generic SCALAR {...} block in all the other scalar contexts. it's a fallback hierarchy, bestie.

another way to picture it: the scalar context blocks form a little family tree ๐ŸŒณ:

    SCALAR
         ^
         |
         |--< BOOL
         |
         |--< NUM
         |
         `--< STR

Contextual::Return uses that hierarchy to pick the most specific context block available for whatever return context it's in โ€” it climbs up the tree from the specific type it needs toward the more general type, if that's all it's got to work with.

now, there are two slight complications to this picture. first one: Perl treats strings and numbers as interconvertable, so the diagram (and the module!) also has to allow those interconversions as a fallback strategy:

    SCALAR
         ^
         |
         |--< BOOL
         |
         |--< NUM
         |    : ^
         |    v :
         `--< STR

those dotted lines mean this interconversion is secondary to the main hierarchical fallback. like, in a numeric context, a STR {...} block only gets used if there's no NUM {...} block and no SCALAR {...} block. in other words: the generic context type always wins over string<->number conversion. periodt.

the second complication: that diagram only shows a tiny slice of the complete hierarchy of contexts Contextual::Return supports. the full fallback hierarchy (dotted interconversions included) is:

      DEFAULT
         ^
         |
         |--< VOID
         |
         `--< NONVOID
                 ^
                 |
                 |--< VALUE <...............
                 |      ^                   :
                 |      |                   :
                 |      |--< SCALAR <.......:...
                 |      |           ^           :
                 |      |           |           :
                 |      |           |--< BOOL   :
                 |      |           |     ^     :
                 |      |           |     |     :
                 |      |           |  PUREBOOL :
                 |      |           |           :
                 |      |           |--< NUM <..:.
                 |      |           |    : ^      :
                 |      |           |    v :      :
                 |      |           `--< STR <....:..
                 |      |                           :
                 |      |                          ::
                 |      `--< LIST ................: :
                 |            : ^                   :
                 |            : :                   :
                 `--- REF     : :                   :
                       ^      : :                   :
                       |      v :                   :
                       |--< ARRAYREF                :
                       |                            :
                       |--< SCALARREF .............:
                       |
                       |--< HASHREF
                       |
                       |--< CODEREF
                       |
                       |--< GLOBREF
                       |
                       `--< OBJREF <....... METHOD
                               ^
                               :........... BLESSED

same as before: every dashed arrow is a fallback relationship. so if the context specifier you need isn't available, follow the arrows until you hit a more generic one. and the dotted arrows are, again, the interconversion of return values โ€” only attempted after the normal hierarchical fallback has already failed. ๐Ÿ’€

for example, if a sub gets called in a context that expects a scalar reference, but there's no SCALARREF {...} block provided, Contextual::Return tries these blocks in order:

        REF {...}
    NONVOID {...}
    DEFAULT {...}
        STR {...} (automatically taking a reference to the result)
        NUM {...} (automatically taking a reference to the result)
     SCALAR {...} (automatically taking a reference to the result)
      VALUE {...} (automatically taking a reference to the result)

likewise, in a list context, if there's no LIST {...} context block, the module tries:

       VALUE {...}
     NONVOID {...}
     DEFAULT {...}
    ARRAYREF {...} (automatically dereferencing the result)
         STR {...} (treating it as a list of one element)
         NUM {...} (treating it as a list of one element)
      SCALAR {...} (treating it as a list of one element)

the more generic context blocks are especially handy for catching unexpected and undesirable call contexts. like, to turn off that automatic scalar-ref and array-ref interpolative behaviour we talked about in "Interpolative referential contexts", you could intercept all referential contexts with a generic REF {...} context block:

    sub get_todo_tasks {
        return (
            SCALAR { scalar @todo_list }      # How many?
            LIST   { @todo_list        }      # What are they?

            REF { croak q{get_todo_task() can't be used as a reference} }
        );
    }

    print 'There are ', get_todo_tasks(), '...';    # Still okay
    print "There are ${get_todo_tasks()}...";       # Throws an exception

caught in 4k ๐Ÿšจ


๐Ÿ‘‘ Treating return values as objects ๐Ÿ‘‘

normally, when a return value gets treated as an object (i.e. somebody calls a method on it), Contextual::Return invokes whatever OBJREF handler you specified in the contextual return list, and delegates the method call to the object that handler returned. easy.

but you can also be more specific, by giving a METHOD context handler in the contextual return list. the block of this handler is expected to return one or more method-name/method-handler pairs, like so:

    return
        METHOD {
            get_count => sub { my $n = shift; $data[$n]{count} },
            get_items => sub { my $n = shift; $data[$n]{items} },
            clear     => sub { @data = (); },
            reset     => sub { @data = (); },
        }

Then, whenever one of the specified methods gets called on the return value, the matching subroutine swoops in to implement it. โœจ

The method handlers must always be subroutine references, no cap. But the method-name specifiers? Those can be strings (like in the example above), or you can get generic with them โ€” as regexes, or as array references. Generic method names are literally how you make one handler serve two or more distinct method names at once. So the previous example could be simplified to:

    return
        METHOD {
            qr/get_(\w+)/     => sub { my $n = shift; $data[$n]{$1} },
            ['clear','reset'] => sub { @data = (); },
        }

A method name specified by regex will invoke the corresponding handler for any method call request that the regex matches. A method name specified by array ref will invoke the corresponding handler if the method requested matches any of the elements of that array (and those elements can themselves be strings or regexes, bestie, it's flexible like that ๐Ÿฆ‹).

When the method handler is invoked, the name of the method requested gets passed to the handler in $_, and the method's argument list gets passed (as usual) via @_.

Heads up: any methods not explicitly handled by the METHOD handlers will still be delegated to the object returned by the OBJREF handler โ€” assuming OBJREF is also specified. ๐Ÿ‘‘

๐ŸŒธ Not treating return values as objects ๐ŸŒธ

The use of OBJREF and METHOD is slightly complicated by the fact that contextual return values are themselves objects. It's giving existential crisis tbh. ๐Ÿ˜ญ

For example, prior to version 0.4.4 of the module, if you passed a contextual return value to Scalar::Util::blessed(), it always returned a true value (namely, the string: 'Contextual::Return::Value'), even if the return value had not specified handlers for OBJREF or METHOD.

In other words, the implementation of contextual return values (as objects) was getting in the way of the use of contextual return values (as non-objects). The object-ness was blocking the whole vibe. ๐Ÿ’€

So the module now also provides a BLESSED handler, which lets you explicitly control how contextual return values interact with Scalar::Util::blessed().

If $crv is a contextual return value, by default Scalar::Util::blessed($crv) will now only return true if that return value has an OBJREF, LAZY, REF, SCALAR, VALUE, NONVOID, or DEFAULT handler that in turn returns a blessed object.

However if $crv also provides a BLESSED handler, blessed() will return whatever that handler returns. That's it. That's the whole deal. ๐Ÿซถ

This means:

    sub simulate_non_object {
        return BOOL { 1 }
                NUM { 42 }
    }

    sub simulate_real_object {
        return OBJREF { bless {}, 'My::Class' }
                 BOOL { 1 }
                  NUM { 42 }
    }

    sub simulate_faked_object {
        return BLESSED { 'Foo' }
                  BOOL { 1 }
                   NUM { 42 }
    }

    sub simulate_previous_behaviour {
        return BLESSED { 'Contextual::Return::Value' }
                  BOOL { 1 }
                   NUM { 42 }
    }

    say blessed( simulate_non_object()         );   # undef
    say blessed( simulate_real_object()        );   # My::Class
    say blessed( simulate_faked_object()       );   # Foo
    say blessed( simulate_previous_behaviour() );   # Contextual::Return::Value

Typically, you either want no BLESSED handler at all (in which case contextual return values pretend not to be blessed objects, iconic denial behavior ๐Ÿ’…), or you want BLESSED { 'Contextual::Return::Value' } for backwards compatibility with pre-v0.4.7 behaviour.

Preventing fallbacks

Sometimes fallbacks can be too helpful. Like, bestie, I appreciate you but please. Or sometimes you want to impose strict type checking on a return value.

Contextual::Returns allows that via the STRICT specifier. If you include STRICT anywhere in your return statement, the module disables all fallbacks and will therefore through an exception if the return value is used in any way not explicitly specified in the contextual return sequence. No sneak-attempts, no loopholes. ๐Ÿ”ฅ

For example, to create a subroutine that returns only a string:

    sub get_name {
        return STRICT STR { 'Bruce' }
    }

If the return value of the subroutine is used in any other way than as a string, an exception will be thrown. Periodt.

You can still specify handlers for more than a single kind of context when using STRICT:

    sub get_name {
        return STRICT
            STR  { 'Bruce' }
            BOOL { 0 }
    }

...but these will still be the only contexts in which the return value can be used:

    my $n = get_name() ? 1 : 2;  # Okay because BOOL handler specified

    my $n = 'Dr' . get_name();   # Okay because STR handler specified

    my $n = 1 + get_name();      # Exception thrown because no NUM handler

In other words, STRICT allows you to impose strict type checking on your contextual return value. Main character boundaries. ๐ŸŽ€

Deferring handlers

Because the various handlers form a hierarchy, it's possible to implement more specific handlers by falling back on ("deferring to") more general ones. For example, a PUREBOOL handler is almost always identical in its basic behaviour to the corresponding BOOL handler, except that it adds some side-effect. For example:

    return
        PUREBOOL { $_ = $return_val; defined $return_val && $return_val > 0 }
            BOOL {                   defined $return_val && $return_val > 0 }
          SCALAR {                   $return_val;                           }

So Contextual::Return allows you to have a handler perform some action and then defer to a more general handler to supply the actual return value. To fall back to a more general case in this way, you simply write:

    next handler;

at the end of the handler in question, after which Contextual::Return will find the next-most-specific handler and execute it as well. So the previous example could be re-written:

    return
        PUREBOOL { $_ = $return_val; next handler;        }
            BOOL { defined $return_val && $return_val > 0 }
          SCALAR { $return_val;                           }

Note that any specific handler can defer to a more general one in this same way. For example, you could provide consistent and maintainable type-checking for a subroutine that returns references by providing ARRAYREF, HASHREF, and SCALARREF handlers that all defer to a generic REF handler, like so:

    my $retval = _get_ref();

    return
       SCALARREF { croak 'Type mismatch' if ref($retval) ne 'SCALAR';
                   next handler;
                 }
        ARRAYREF { croak 'Type mismatch' if ref($retval) ne 'ARRAY';
                   next handler;
                 }
         HASHREF { croak 'Type mismatch' if ref($retval) ne 'HASH';
                   next handler;
                 }
             REF { $retval }

If, at a later time, the process of returning a reference became more complex, only the REF handler would have to be updated. DRY queen behavior, we love to see it. ๐Ÿ‘‘

Nested handlers

Another way of factoring out return behaviour is to nest more specific handlers inside more general ones. For instance, in the final example given in "Boolean vs Pure Boolean contexts":

    sub get_data {
        my $data;
        return
            PUREBOOL {        _check_for_but_dont_get_data();   }
                BOOL { defined( $data ||= _go_and_get_data() ); }
                 REF {          $data ||= _go_and_get_data();   }
    }

you could factor out the repeated calls to _go_and_get_data() like so:

    sub get_data {
        return
            PUREBOOL { _check_for_but_dont_get_data(); }
             DEFAULT {
                my $data = _go_and_get_data();

                BOOL { defined $data; }
                 REF {         $data; }
             }
    }

Here, the DEFAULT handler deals with every return context except pure boolean. Within that DEFAULT handler, the data is first retrieved, and then two "sub-handlers" deal with the ordinary boolean and referential contexts. Nested like matryoshka dolls but make it code. ๐Ÿช†โœจ

Typically nested handlers are used in precisely this way: to optimize for inexpensive special cases (such as pure boolean or integer or void return contexts) and only do extra work for those other cases that require it. Efficiency slay. ๐Ÿ’…

Failure contexts

Two of the most common ways to specify that a subroutine has failed are to return a false value, or to throw an exception. The Contextual::Return module provides a mechanism that allows the subroutine writer to support both of these mechanisms at the same time, by using the FAIL specifier. Why choose one when you can have both, right? ๐Ÿ˜Œ

A return statement of the form:

    return FAIL;

causes the surrounding subroutine to return undef (i.e. false) in boolean contexts, and to throw an exception in any other context. For example:

    use Contextual::Return;

    sub get_next_val {
        my $next_val = <>;
        return FAIL if !defined $next_val;
        chomp $next_val;
        return $next_val;
    }

If the return FAIL statement is executed, it will either return false in a boolean context:

    if (my $val = get_next_val()) {      # returns undef if no next val
        print "[$val]\n";
    }

or else throw an exception if the return value is used in any other context:

    print get_next_val();       # throws exception if no next val

    my $next_val = get_next_val();
    print "[$next_val]\n";      # throws exception if no next val

The exception that is thrown is of the form:

    Call to main::get_next_val() failed at demo.pl line 42

but you can change that message by providing a block to the FAIL, like so:

    return FAIL { "No more data" } if !defined $next_val;

in which case, the final value of the block becomes the exception message:

    No more data at demo.pl line 42

A failure value can be interrogated for its error message, by calling its error() method, like so:

    my $val = get_next_val();
    if ($val) {
        print "[$val]\n";
    }
    else {
        print $val->error, "\n";
    }

Configurable failure contexts

The default FAIL behaviour โ€” false in boolean context, fatal in all others โ€” works well in most situations, but it violates the Platinum Rule ("Do unto others as they would have done unto them"). ๐Ÿ˜ญ

So like, lowkey it'd be way friendlier if whoever's using a module got to decide how that module's subs act when stuff goes wrong, right? Bestie A might want failing subs to always hand back undef; bestie B might want them to always throw; bestie C might want them to log the mess and return a special Failure object; and bestie D might want 0 in scalar context, an empty list in list context, and an exception everywhere else. Everyone's got their vibe! ๐Ÿ’…

You could build a module that lets your user pick all those alternatives, like this:

package MyModule;
use Contextual::Return;
use Log::StdLog;

sub import {
    my ($package, @args) = @_;

    Contextual::Return::FAIL_WITH {
        ':false' => sub { return undef },
        ':fatal' => sub { croak @_       },
        ':filed' => sub {
                        print STDLOG 'Sub ', (caller 1)[3], ' failed';
                        return Failure->new();
                    },
        ':fussy' => sub {
                        SCALAR  { undef      }
                        LIST    { ()         }
                        DEFAULT { croak @_ }
                    },
    }, @args;
}

So this configures Contextual::Return so that instead of the usual false-or-fatal drama, every single return FAIL inside MyModule's namespace gets implemented by one of those four subroutines you passed in that hash to FAIL_WITH. Iconic. โœจ

Which of the four subs actually implements the FAIL is decided by the arguments that come after the hash โ€” aka by whatever's chilling in @args. FAIL_WITH strolls through that argument list and compares each one against the hash keys. If a key matches an argument, that key's value becomes the implementation of FAIL. But heads up: if later arguments also match a key, their subroutine overrides the one that was installed before, so only the final override actually does anything. Contextual::Return will throw warnings when you specify multiple overrides, so don't be messy. ๐Ÿ™ˆ

All of which means: if a user loaded MyModule like this:

use MyModule qw( :fatal other args here );

then every FAIL inside MyModule would get reconfigured to throw an exception in all circumstances, because that ':fatal' sitting in the argument list makes FAIL_WITH pick the hash entry whose key is ':fatal'. Periodt.

But on the other hand, if they loaded the module:

use MyModule qw( :fussy other args here );

then each FAIL inside MyModule would return undef or an empty list or throw an exception, all depending on context, since that's literally what the sub keyed ':fussy' does. Context queen. ๐Ÿ‘‘

Lots of people are obsessed with module interfaces that use a flag => value format, and guess what โ€” FAIL_WITH supports that too. Say you wanted your module to take a -fail flag whose value could be any of undefined, exception, logged, or context. You'd implement it by just specifying the flag as the first argument (i.e. before the hash), like so:

sub import {
    my $package = shift;

    Contextual::Return::FAIL_WITH -fail => {
        'undefined' => sub { return undef },
        'exception' => sub { croak @_ },
        'logged'    => sub {
                        print STDLOG 'Sub ', (caller 1)[3], ' failed';
                        return Failure->new();
                    },
        'context' => sub {
                        SCALAR  { undef      }
                        LIST    { ()         }
                        DEFAULT { croak @_ }
                    },
    }, @_;

and then load the module:

use MyModule qw( other args here ), -fail=>'undefined';

or:

use MyModule qw( other args here ), -fail=>'exception';

In this case, FAIL_WITH scans the argument list looking for a pair of values: its flag string, followed by some other selector value. Then it looks up that selector value in the hash and installs the matching subroutine as its local FAIL handler. Chef's kiss. ๐ŸŽ€

If this "flagged" interface is what you're using, your module's user can also specify their own handler directly โ€” just pass a subroutine reference as the selector value instead of a string:

use MyModule qw( other args here ), -fail=>sub{ die 'horribly'};

If this last example were used, any call to FAIL inside MyModule would invoke that anonymous subroutine you specified (and therefore throw a 'horribly' exception). Dramatic, but valid. ๐Ÿ’€

Now, important: any overriding of a FAIL handler is specific to the namespace and file from which the subroutine that calls FAIL_WITH is itself called. Since FAIL_WITH is designed to be called from inside a module's import() subroutine, that generally means the FAILs within a given module X are only overridden for the current namespace within the particular file that module X is loaded from. Which is actually great news: two separate pieces of code (in separate files or separate namespaces) can each independently override a module's FAIL behaviour without interfering with each other. No drama, no crossover. ๐Ÿฆ‹

โœจ Lvalue contexts โœจ

Recent versions of Perl offer (limited!) support for lvalue subroutines โ€” subs that return a modifiable variable instead of just a simple constant value. Main character behaviour, honestly.

Contextual::Return can make creating those subs easier, within the limits Perl itself imposes. And the limitations Perl places on lvalue subs are:

  1. The subroutine must be declared with an :lvalue attribute:
sub foo :lvalue {...}
  1. The subroutine must not return via an explicit return. Instead, the last statement has to evaluate to a variable, or be a call to another lvalue subroutine call.
my ($foo, $baz);

sub foo :lvalue {
    $foo;               # last statement evals to a var
}

sub bar :lvalue {
    foo();              # last statement is lvalue sub call
}

sub baz :lvalue {
    my ($arg) = @_;

    $arg > 0            # last statement evals...
        ? $baz          # ...to a var
        : bar();        # ...or to an lvalue sub call
}

From then on, any call to the lvalue subroutine produces a result you can assign to:

baz(0) = 42;            # same as: $baz = 42

baz(1) = 84;            # same as:                  bar() = 84
                        #  which is the same as:    foo() = 84
                        #   which is the same as:   $foo  = 84

At the end of the day, every lvalue subroutine has to return a scalar variable, which then gets used as the lvalue of the assignment (or whatever other lvalue operation is applied to the sub call). Unfortunately โ€” because the sub has to return that variable before the assignment can happen โ€” there's no way a normal lvalue sub can get access to the value that's eventually going to be assigned to its return value. Tragic, lowkey. ๐Ÿ˜ญ

And that's occasionally annoying, so Contextual::Return swoops in with a solution: on top of all the context blocks described above, it gives you three special contextual return blocks made specifically for lvalue subroutines โ€” LVALUE, RVALUE, and NVALUE. Slay. ๐Ÿ’–

Using these blocks you can specify what happens when an lvalue sub is used in lvalue and non-lvalue (rvalue) context. For example:

my $verbosity_level = 1;

# Verbosity values must be between 0 and 5...
sub verbosity :lvalue {
    LVALUE { $verbosity_level = max(0, min($_, 5)) }
    RVALUE { $verbosity_level                      }
}

The LVALUE block fires whenever verbosity is called as an lvalue:

verbosity() = 7;

The block gets access to the value being assigned, which is passed to it as $_. So in the example above, the assigned value of 7 would be aliased to $_ inside the LVALUE block, would get reduced to 5 by that "min-of-max" expression, and then assigned to $verbosity_level. Math but make it โœจaestheticโœจ.

(Oh and if you need to reach the caller's $_, it's still available too: as $CALLER::_.)

When the sub isn't used as an lvalue:

print verbosity();

the RVALUE block runs instead, and its final value gets returned. And inside an RVALUE block you can use any of the other Contextual::Return features. Like:

sub verbosity :lvalue {
    LVALUE { $verbosity_level = int max(0, min($_, 5)) }
    RVALUE {
        NUM  { $verbosity_level               }
        STR  { $description[$verbosity_level] }
        BOOL { $verbosity_level > 2           }
    }
}

but just know the context sequence has to be nested inside an RVALUE block. Don't fight it. ๐Ÿ™ˆ

You can also specify what an lvalue sub should do when it's used neither as an lvalue nor as an rvalue (i.e. in void context), by using an NVALUE block:

sub verbosity :lvalue {
    my ($level) = @_;

    NVALUE { $verbosity_level = int max(0, min($level, 5)) }
    LVALUE { $verbosity_level = int max(0, min($_,     5)) }
    RVALUE {
        NUM  { $verbosity_level               }
        STR  { $description[$verbosity_level] }
        BOOL { $verbosity_level > 2           }
    }
}

In this example, a call to verbosity() in void context sets the verbosity level to whatever argument you passed to the sub:

verbosity(1);
Note that you cannot get the same effect by nesting a VOID block inside an RVALUE block:
    LVALUE { $verbosity_level = int max(0, min($_, 5)) }
    RVALUE {
        NUM  { $verbosity_level               }
        STR  { $description[$verbosity_level] }
        BOOL { $verbosity_level > 2           }
        VOID { $verbosity_level = $level      }  # Wrong!
    }

That's because in a void context the return value is never evaluated, so it's never treated as an rvalue โ€” which means the RVALUE block never even executes. It's giving "nothing happened and nobody knows why." ๐Ÿ’€

๐ŸŒธ Result blocks ๐ŸŒธ

Every once in a while it's just convenient to calculate a return value before the end of a contextual return block. Like, maybe you need to clean up external resources that were involved in the calculation once it's done. Typically this requirement produces a slightly awkward little code sequence like this:

return
    VALUE {
        $db->start_work();
        my $result = $db->retrieve_query($query);
        $db->commit();
        $result;
    }

And those code sequences get considerably more awkward when you want the return value to be context sensitive, in which case you have to write either:

so bestie, imagine you write this:

           return
               LIST {
                   $db->start_work();
                   my @result = $db->retrieve_query($query);
                   $db->commit();
                   @result;
               }
               SCALAR {
                   $db->start_work();
                   my $result = $db->retrieve_query($query);
                   $db->commit();
                   $result;
               }

or, worse:

           return
               VALUE {
                   $db->start_work();
                   my $result = LIST ? [$db->retrieve_query($query)]
                                     :  $db->retrieve_query($query);
                   $db->commit();
                   LIST ? @{$result} : $result;
               }

okay so to dodge all that mess, Contextual::Return gives you a second way to set the result of a context block โ€” one that does NOT require the result to be the last statement in the block. iconic, right? ๐Ÿ’…

           return
               LIST {
                   $db->start_work();
                   RESULT { $db->retrieve_query($query) };
                   $db->commit();
               }
               SCALAR {
                   $db->start_work();
                   RESULT { $db->retrieve_query($query) };
                   $db->commit();
               }

so when you put a RESULT block inside a contextual return block, that block returns the value of the final statement of the RESULT block as the handler's return value, instead of returning the value of the handler's own final statement. basically, the RESULT block overrides the normal return value of a context handler. periodt.

even better? the RESULT block always evaluates its final statement in the same context as the surrounding return, so you can literally just write:

           return
               VALUE {
                   $db->start_work();
                   RESULT { $db->retrieve_query($query) };
                   $db->commit();
               }

and the retrieve_query() method will be called in the appropriate context in all cases.

a RESULT block can appear anywhere inside any contextual return block, but it may NOT be used outside a context block. so this? this is an error:

           if ($db->closed) {
               RESULT { undef }; # Error: not in a context block
           }
           return
               VALUE {
                   $db->start_work();
                   RESULT { $db->retrieve_query($query) };
                   $db->commit();
               }

๐Ÿงผ Post-handler clean-up ๐Ÿงผ

okay so if a subroutine uses an external resource, you often need to close or clean up that resource after the subroutine ends... doesn't matter if it exits normally or via an exception. it's giving responsible. โœจ

usually you do this by wrapping the resource in a lexically scoped object whose destructor handles the clean-up. but if the clean-up doesn't involve deallocating an object (like in the $db->commit() example from the last section), it's lowkey annoying to have to create a whole class and allocate a container object just to mediate the clean-up. not the vibe.

to make managing those resources easier, Contextual::Return supplies a special labelled block: the RECOVER block. if a RECOVER block is part of a contextual return sequence, it gets executed after any context handler โ€” even if the context handler exits via an exception. slay. ๐Ÿ’…

so, for example, you could implement a simple commit-or-revert policy like so:

           return
               LIST    { $db->retrieve_all($query)  }
               SCALAR  { $db->retrieve_next($query) }
               RECOVER {
                   if ($@) {
                       $db->revert();
                   }
                   else {
                       $db->commit();
                   }
               }

the presence of a RECOVER block also intercepts all exceptions thrown in any other context block in the same contextual return sequence. any such exception is passed into the RECOVER block the usual way: via the $@ variable. and you can rethrow the exception out of the RECOVER block by calling die:

           return
               LIST    { $db->retrieve_all($query) }
               DEFAULT { croak "Invalid call (not in list context)" }
               RECOVER {
                   die $@ if $@;    # Propagate any exception
                   $db->commit();   # Otherwise commit the changes
               }

a RECOVER block can also access or replace the returned value by invoking a RESULT block. for example:

           return
               LIST    { attempt_to_generate_list_for(@_)  }
               SCALAR  { attempt_to_generate_count_for(@_) }
               RECOVER {
                   if ($@) {                # On any exception...
                       warn "Replacing return value. Previously: ", RESULT;
                       RESULT { undef }     # ...return undef
                   }
               }

โ™ป๏ธ Post-return clean-up โ™ป๏ธ

sometimes you need to defer the clean-up of resources until after the return value has been used. once again, this is usually done by returning an object with a suitable destructor.

with Contextual::Return you can get the same effect by providing a CLEANUP block in the contextual return sequence:

           return
               LIST    { $db->retrieve_all($query)  }
               SCALAR  { $db->retrieve_next($query) }
               CLEANUP { $db->commit()              }

in this example, the commit method call only happens after the return value has been used by the caller. note that this is quite different from using a RECOVER block, which is called as the subroutine returns its value; a CLEANUP is called when the returned value is garbage collected. iykyk.

a CLEANUP block is useful for controlling resources allocated to support an ACTIVE return value. for example:

           my %file;

           # Return an active value that is always the next line from a file...
           sub readline_from {
               my ($file_name) = @_;

               # Open the file, if not already open...
               if (!$file{$file_name}) {
                   open $file{$file_name}{handle}, '<', $file_name;
               }

               # Track how many active return values are using this file...
               $file{$file_name}{count}++;

               return ACTIVE
                   # Evaluating the return value returns the next line...
                   VALUE   { readline $file{$file_name}{handle} }

                   # Once the active value is finished with, clean up the filehandle...
                   CLEANUP {
                       delete $file{$file_name}
                           if --$file{$file_name}{count} == 0;
                   }
           }

๐Ÿ› Debugging contextual return values ๐Ÿ›

contextual return values are implemented as opaque objects (using the inside-out technique). this means that passing such values to Data::Dumper produces an uninformative output like:

           $VAR1 = bless( do{\(my $o = undef)}, 'Contextual::Return::Value' );

so the module provides two methods that let contextual return values be correctly reported: either directly, or when dumped by Data::Dumper.

to dump a contextual return value directly, call the module's DUMP() method explicitly and print the result:

           print $crv->Contextual::Return::DUMP();

this produces an output something like:

           [
            { FROM       => 'main::foo'                                       },
            { NO_HANDLER => [ 'VOID', 'CODEREF', 'HASHREF', 'GLOBREF' ]       },
            { FALLBACKS  => [ 'VALUE' ]                                       },
            { LIST       => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]                 },
            { STR        => '<<<Throws exception: Died at demo.pl line 7.>>>' },
            { NUM        => 42                                                },
            { BOOL       => -1                                                },
            { SCALARREF  => '<<<self-reference>>>'                            },
            { ARRAYREF   => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]                 },
           ];

the FROM hash entry names the subroutine that produced the return value. the NO_HANDLER hash entry lists those contexts for which no handler was defined (and which would therefore normally produce can't call exceptions like: Can't call main::foo in VOID context). the FALLBACKS hash entry lists any generic contexts such as VALUE, NONVOID, REF, DEFAULT, etc. that the contextual return value can also handle. after these, all the remaining hash entries are actual contexts in which the return value could successfully be evaluated, and the value it would produce in each of those contexts.

the Data::Dumper module also has a mechanism where you can tell it how to produce a similar listing automatically whenever a contextual return value is passed to its Dumper method. Data::Dumper lets you register a freezer method that is called prior to dumping, and which can be used to adapt an opaque object to make it dumpable. Contextual::Return provides just such a method (Contextual::Return::FREEZE()) for you to register, like so:

           use Data::Dumper 'Dumper';

           local $Data::Dumper::Freezer = 'Contextual::Return::FREEZE';

           print Dumper $foo;

the output is then precisely the same as Contextual::Return::DUMP() would produce.

note that, with both of the above dumping mechanisms, it is essential to use the full name of the method. that is:

           print $crv->Contextual::Return::DUMP();

rather than:

           print $crv->DUMP();

this is because the shorter version is interpreted as calling the DUMP() method on the object returned by the return value's OBJREF context block (see Scalar reference contexts).

for the same reason, you must write:

           local $Data::Dumper::Freezer = 'Contextual::Return::FREEZE';

not:

           local $Data::Dumper::Freezer = 'FREEZE';

๐ŸŽ›๏ธ Namespace controls ๐ŸŽ›๏ธ

by default the module exports a large number of return context markers:

           DEFAULT    REF          LAZY
           VOID       SCALARREF    FIXED
           NONVOID    ARRAYREF     ACTIVE
           LIST       CODEREF      RESULT
           SCALAR     HASHREF      RECOVER
           VALUE      GLOBREF      CLEANUP
           STR        OBJREF       RVALUE
           NUM        METHOD       LVALUE
           BOOL                    NVALUE
           PUREBOOL

these are exported as subroutines, so they can conflict with existing subroutines in your namespace, or with subroutines imported from other modules. not the vibe. ๐Ÿ˜ญ

Contextual::Return lets you control which contextual return blocks are exported into any namespace that uses the module. it also lets you rename blocks to avoid namespace conflicts with existing subroutines.

both these features are controlled by passing arguments to the use statement that loads the module, like this:

  • any string passed as an argument to use Contextual::Return, exports only the block name it specifies;
  • any regex passed as an argument to use Contextual::Return exports every block name it matches;
  • Any array ref (recursively) exports each of its elements ๐Ÿซถ
  • Any string that appears immediately after one of the above three specifiers, and which is not itself a block name, renames the handlers exported by that preceding specifier by filtering each handler name through sprintf()

So like โ€” that means you can name your handlers exactly (as a string), by general pattern (as a regex), or all together (in an array). And after any of those export specs, you can tack on a template where any '%s' gets swapped out for the original name of the handler. Obsessed. Watch:

# Selectively export specific sets of handlers...
use Contextual::Return  qr/[NLR]VALUE/;
use Contextual::Return  qr/.*REF/;

# Selective export specific sets and add a suffix to each...
use Contextual::Return  qr/[NLR]VALUE/ => '%s_CONTEXT';

# Selective export specific sets and add a prefix to each...
use Contextual::Return  qr/.*REF/ => 'CR_%s';

# Export a list of handlers...
use Contextual::Return    'NUM', 'STR', 'BOOL' ;
use Contextual::Return qw< NUM    STR    BOOL >;
use Contextual::Return   ['NUM', 'STR', 'BOOL'];

# Export a list of handlers, renaming them individually...
use Contextual::Return  NUM => 'NUMERIC', STR => 'TEXT', BOOL => 'CR_%s';

# Export a list of handlers, renaming them collectively...
use Contextual::Return  ['NUM', 'STR', 'BOOL'] => '%s_CONTEXT';

# Mixed exports and renames...
use Contextual::Return (
    STR => 'TEXT',
    ['NUM', 'BOOL'] => 'CR_%s',
    ['LIST', 'SCALAR', 'VOID', qr/^[NLR]VALUE/] => '%s_CONTEXT',
);

โœจ INTERFACE โœจ

๐Ÿงช Context tests

  • LIST() โ€” true if the current sub got called in list context. A cleaner way of writing: wantarray() ๐Ÿ’–
  • SCALAR() โ€” true if the current sub got called in scalar context. A cleaner way of writing: defined wantarray() && ! wantarray()
  • VOID() โ€” true if the current sub got called in void context. A cleaner way of writing: !defined wantarray()
  • NONVOID() โ€” true if the current sub got called in list or scalar context. A cleaner way of writing: defined wantarray()

๐ŸŽ€ Standard contexts

  • LIST {...} โ€” this block says what the context sequence should evaluate to when it's called in list context.
  • SCALAR {...} โ€” this block says what the context sequence should evaluate to in scalar contexts... unless some more-specific scalar context specifier (peep below ๐Ÿ‘€) also shows up in the same context sequence.
  • VOID {...} โ€” this block says what the context sequence should do when it's called in void context.

๐Ÿ’… Scalar value contexts

  • BOOL {...} โ€” this block says what the context sequence should evaluate to when it's treated as a boolean value.
  • NUM {...} โ€” this block says what the context sequence should evaluate to when it's treated as a numeric value.
  • STR {...} โ€” this block says what the context sequence should evaluate to when it's treated as a string value.
  • LAZY {...} โ€” literally just another name for SCALAR {...}. It's usefully self-documenting when the whole point of the contextual return is to defer evaluation of the return value until it's actually required. iykyk โœจ

๐ŸŒˆ Scalar reference contexts

  • SCALARREF {...} โ€” this block says what the context sequence should evaluate to when it's treated as a reference to a scalar.
  • ARRAYREF {...} โ€” this block says what the context sequence should evaluate to when it's treated as a reference to an array.

HASHREF {...}

This block says what the context sequence should evaluate to when it's treated as a reference to a hash.

โš ๏ธ Okay bestie, listen โ€” the classic mistake here is writing: HASHREF { a=>1, b=>2, c=>3 } Those curly braces are a block, not a hash constructor, so the block doesn't return a hash reference and the interpreter straight-up throws an exception. ๐Ÿ˜ญ What you actually need is: HASHREF { {a=>1, b=>2, c=>3} } where the inner braces are the hash constructor. the more you know ๐Ÿ’ซ
  • CODEREF {...} โ€” this block says what the context sequence should evaluate to when it's treated as a reference to a subroutine.
  • GLOBREF {...} โ€” this block says what the context sequence should evaluate to when it's treated as a reference to a typeglob.
  • OBJREF {...} โ€” this block says what the context sequence should evaluate to when it's treated as a reference to an object.
  • METHOD {...} โ€” this block lets you specify particular handlers for specific method calls when the return value is treated as an object reference. It should return a list of methodname/methodbody pairs. Each method name can be a string, a regex, or an array of strings or regexes. The method bodies must be subroutine references (usually anonymous subs). The first method name that matches the actual method call picks the corresponding handler, which then gets called. no cap ๐Ÿ‘‘

๐Ÿฆ‹ Generic contexts

  • VALUE {...} โ€” this block says what the context sequence should evaluate to when it's treated as a non-referential value (as a boolean, numeric, string, scalar, or list). Only used if there's no more-specific value context specifier in the context sequence.
  • REF {...} โ€” this block says what the context sequence should evaluate to when it's treated as a reference of any kind. Only used if there's no more-specific referential context specifier in the context sequence.
  • NONVOID {...} โ€” this block says what the context sequence should evaluate to when it's used in a non-void context of any kind. Only used if there's no more-specific context specifier in the context sequence.
  • DEFAULT {...} โ€” this block says what the context sequence should evaluate to when it's used in a void or non-void context of any kind. Only used if there's no more-specific context specifier in the context sequence. the catch-all icon ๐Ÿ’…

๐Ÿ’€ Failure context

FAIL

This block runs unconditionally and is how you signal failure. In a Boolean context it returns false. In every other context it throws an exception made out of the final evaluated value of the block.

So like, using FAIL:

return FAIL { "Could not defenestrate the widget" }

is exactly equivalent to writing:

return BOOL { 0 } DEFAULT { croak "Could not defenestrate the widget" }

except that the reporting of errors is a little smarter under FAIL. โœจ

If FAIL gets called without specifying a block:

return FAIL;

it's equivalent to:

return FAIL { croak "Call to <subname> failed" }

(where <subname> is replaced with the name of the surrounding subroutine).

๐Ÿšจ Note that, because FAIL implicitly covers every possible return context, it cannot be chained with other context specifiers. periodt.

Contextual::Return::FAIL_WITH

This subroutine is not exported, but you can call it directly to reconfigure FAIL behaviour in the caller's namespace.

You call it with an optional string (the flag), then a mandatory hash reference (the configurations hash), then a list of zero-or-more strings (the selector list). Every value in the configurations hash must be a subroutine reference.

If the optional flag is given, FAIL_WITH searches the selector list looking for that string, then uses the following item in the selector list as its selector value. If that selector value is a string, FAIL_WITH looks up that key in the hash and installs the corresponding subroutine as the namespace's FAIL handler (an exception is thrown if the selector string isn't a valid key of the configurations hash). If the selector value is a subroutine reference, FAIL_WITH installs that subroutine as the FAIL handler. slay ๐Ÿซถ

If the optional flag isn't given, FAIL_WITH searches the entire selector list looking for the last element that matches any key in the configurations hash. Then it looks up that key in the hash, and installs the corresponding subroutine as the namespace's FAIL handler.

See "Configurable failure contexts" for examples of using this feature. ๐Ÿ‘€


๐Ÿ”ฅ Lvalue contexts

  • LVALUE โ€” this block runs when the result of an :lvalue subroutine is assigned to. The assigned value gets passed to the block as $_. To reach the caller's $_ value, use $CALLER::_.
  • RVALUE โ€” this block runs when the result of an :lvalue subroutine is used as an rvalue. The final value evaluated in the block becomes the rvalue.
  • NVALUE โ€” this block runs when an :lvalue subroutine is evaluated in void context.

๐Ÿ“ Explicit result blocks

RESULT

This block may only appear inside a context handler block. It makes the surrounding handler return the final value of the RESULT's block, instead of the final value of the handler's own block. This override happens no matter where the RESULT block sits inside the handler. main character energy, honestly ๐ŸŒŸ

If it's called without a trailing {...}, it just returns the current result value in scalar contexts, or the list of result values in list context.


๐Ÿงš Recovery blocks

  • RECOVER โ€” if it's present in a context return sequence, this block grabs control after any context handler returns or exits via an exception. If an exception was thrown, it gets passed to the RECOVER block via the $@ variable.

๐ŸŒธ Clean-up blocks

  • CLEANUP โ€” if it's present in a context return sequence, this block grabs control when a return value is garbage collected.

โญ Modifiers

  • FIXED โ€” says the scalar value will only be evaluated once, the first time it's used, and that the value will then morph into that evaluated value.
  • ACTIVE โ€” says the scalar value's originating block will be re-evaluated every single time the return value is used.

๐Ÿ‘‘ Debugging support

  • $crv->Contextual::Return::DUMP() โ€” return a dumpable representation of the return value in all viable contexts.
  • local $Data::Dumper::Freezer = 'Contextual::Return::FREEZE';
  • local $Data::Dumper::Freezer = \&Contextual::Return::FREEZE; โ€” configure Data::Dumper to correctly dump a representation of the contextual return value. ๐ŸŽ€

๐Ÿšจ DIAGNOSTICS ๐Ÿšจ

okay bestie, this is the part where the module yells at you. not in a mean way!! more like a concerned friend texting "heyโ€ฆ u good?" ๐Ÿ’– Every one of these is Contextual::Return telling you exactly what went sideways, so read the whole message, don't just screenshot the first line and panic.

  • Can't use %s as export specifier โ€” in your use Contextual::Return line you handed over something (like a hash or a coderef) that literally cannot be used to choose what gets exported. The list of selectors has to be strings, regexes, or references to arrays of strings or regexes. that's it, that's the guest list. ๐ŸŽ€
  • use Contextual::Return qr{%s} didn't export anything โ€” you gave a regex to pick which handlers to support, and that regex matched nothing. zero. goose egg. ๐Ÿ˜ญ Double check that your pattern actually matches at least one of the names of the module's many handlers.
  • Can't export %s: no such handler โ€” you named a context handler to export, but there's no handler by that name. Spelling check, bestie, we've all been there. โœจ
  • Can't call %s in a %s context
  • Can't use return value of %s in a %s context โ€” the sub you called does contextual returns, but it has no clue what to give back in the particular context you called it from. Two options: change the context you're calling in, or add a context block for the offending context (or honestly just a DEFAULT {...} block and call it a day). ๐Ÿ’…
  • Can't call bare %s {...} in %s context โ€” you wrote a handler like VOID {...} or LIST {...} outside any subroutine, in a context it can't handle. Did you mean to put the handler outside a sub? If yes, it needs to live in a context it can actually handle. If no, you probably wanted parens instead of a trailing block โ€” so VOID() or LIST(). ๐Ÿซถ
  • Call to %s at %s didn't return a %s reference" โ€” you called the sub in a context that expected a reference back, but the sub never specified the matching SCALARREF, ARRAYREF, HASHREF, CODEREF, GLOBREF, or the generic REF, NONVOID, or DEFAULT handler. Add the right one and you're golden. โญ
  • Can't call method '%s' on %s value returned by %s" โ€” you called the sub, then tried to call a method on what came back, but the returned classname or object doesn't have that method. Most likely the sub didn't return the classname or object you thought it did. Or maybe you need an OBJREF {...} context block. ๐Ÿ‘‘
  • Can't install two %s handlers โ€” you tried to put two context blocks with the same name in the same return context, which is ambiguous and the module is NOT about that life. For example:
sub foo: lvalue {
    LVALUE { $foo = $_ }
    RVALUE { $foo }
    LVALUE { $foo = substr($_,1,10) }
}

or:

sub bar {
    return
        BOOL { 0 }
        NUM  { 1 }
        STR  { "two" }
        BOOL { 1 };
}

Did you cut-and-paste wrong, or mislabel one of the blocks? (be honest ๐Ÿซฃ)

  • Expected a %s block after the %s block but found instead: %s โ€” if you specify any of LVALUE, RVALUE, or NVALUE, then only LVALUE, RVALUE, or NVALUE blocks can go in that same return context. Need other contexts (like BOOL, STR, REF, etc.)? Nest them inside an RVALUE block. There's a whole example in "Lvalue contexts". ๐Ÿฆ‹
  • Call to %s failed at %s โ€” this is the default exception a FAIL throws in a non-scalar context. Translation: the sub you called signalled failure by throwing, and you did not catch it. So either wrap the call in an eval {...} block, or call the sub in boolean context instead. ๐ŸŒธ
  • Call to %s failed at %s. Attempted to use failure value at %s โ€” this is the default exception a FAIL throws when a failure value gets captured in a scalar variable and then used later in a non-boolean context. Meaning: the sub failed, you never checked the return value for that failure, and then the moment you tried to use that invalid value it killed your program. ๐Ÿ’€ Fix: put the original call in an eval {...}, or test the return value in boolean context and avoid using it if it's false. like, actually avoid it.
  • Usage: FAIL_WITH $flag_opt, \%selector, @args โ€” FAIL_WITH wants an optional flag, then a reference to a configuration hash, then a list or selector arguments. You gave it something else entirely. Peep "Configurable Failure Contexts". ๐Ÿ“‹
  • Selector values must be sub refs โ€” you passed FAIL_WITH a configuration hash that named non-subroutines as possible FAIL handlers. Non-subs can't be handlers, soโ€ฆ did you forget the sub keyword somewhere? ๐Ÿ™ˆ
  • Invalid option: %s =" %s> โ€” FAIL_WITH got a flag/selector pair, but that selector isn't one of the ones the configuration hash allows. ๐Ÿšซ
  • FAIL handler for package %s redefined โ€” just a warning: the FAIL handler for a particular package got reconfigured more than once. Usually that's because the module was loaded in two places with different configurations. And likeโ€ฆ you can't reasonably expect two different sets of behaviour from one module inside one namespace, bestie. pick a lane. ๐ŸŒˆ

โš™๏ธ CONFIGURATION AND ENVIRONMENT โš™๏ธ

Contextual::Return requires no configuration files or environment variables. nothing to set up. no .rc file to hunt down at 3am. it just works. iconic behaviour honestly. โœจ


๐Ÿ“ฆ DEPENDENCIES ๐Ÿ“ฆ

Requires version.pm and Want.pm. that's the whole squad. ๐Ÿ’œ


๐Ÿšซ INCOMPATIBILITIES ๐Ÿšซ

LVALUE, RVALUE, and NVALUE do not work correctly under the Perl debugger. The vibe is that the debugger injects code to capture the return values from subroutines, and that interferes destructively with the optional final arguments that let LVALUE, RVALUE, and NVALUE cascade within a single return. so, debugger + these three = not compatible, no cap. ๐Ÿ˜ญ


๐Ÿ› BUGS AND LIMITATIONS ๐Ÿ›

No bugs have been reported. flawless, unproblematic, nothing to report. (iykyk, enjoy it while it lasts ๐ŸŽ€)


๐Ÿ‘‘ AUTHOR ๐Ÿ‘‘

Damian Conway <DCONWAY@cpan.org>


Copyright (c) 2005-2011, Damian Conway <DCONWAY@cpan.org>. All rights reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


โš–๏ธ DISCLAIMER OF WARRANTY โš–๏ธ

okay bestie this next part is the legal bit and I am not paraphrasing a warranty, so here it is word for word, all caps and everything:

BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

and that's a wrap on the whole page, bestie!! ๐ŸŽ‰ this beauty shipped as part of perl v5.36.0, dated 2022-11-19, filed under Contex...Return(3pm) in the User Contributed Perl Documentation. go forth and return contextually, you absolute legend ๐Ÿ’…โœจ๐ŸŒธ

๐Ÿ“ฆ where this page comes from