NAME
Scope::Escape::Sugar - whizzy syntax for non-local control transfer
SYNOPSIS
use Scope::Escape::Sugar
qw(with_escape_function with_escape_continuation);
{ with_escape_function $e; ...; $e->($r); ...; }
with_escape_function $e { ...; $e->($r); ...; }
$res = with_escape_function($e { ...; $e->($r); ...; });
{ with_escape_continuation $e; ...; $e->($r); ...; }
with_escape_continuation $e { ...; $e->($r); ...; }
$res = with_escape_continuation($e { ...; $e->($r); ...; });
use Scope::Escape::Sugar qw(block return_from);
{ block foo; ...; return_from foo $r; ...; }
block foo { ...; return_from foo $r; ...; }
$res = block(foo { ...; return_from foo $r; ...; });
use Scope::Escape::Sugar qw(catch throw);
{ catch "foo"; ...; }
catch "foo" { ...; }
$res = catch("foo" { ...; });
throw("foo", $r);
DESCRIPTION
This module provides specialised syntax for non-local control transfer (jumping between stack frames), mainly based on the operators in Common Lisp. The non-local control transfers behave exactly like those of Scope::Escape, which should be consulted for the semantic details. This module provides more structured facilities, which take a variety of approaches to referencing the stack frame to be transferred to.
OPERATORS AND FUNCTIONS
The items shown here are mostly not ordinary functions. Most are operators that introduce a form that has some special syntax, not conforming to the ordinary Perl syntax. The documentation shows the complete syntax of forms that use the operator. The complete form may be either a statement or an expression, as indicated in the documentation.
Direct escape continuation access
This facility provides direct access to the continuation functions/objects implemented by Scope::Escape, referenced through lexically-scoped variables. It is just slightly more structured than direct use of Scope::Escape's operators.
In each version, there is a code block and a variable ESCAPE_VAR. ESCAPE_VAR is a lexically-scoped (my
-like) scalar variable. Its name must start with a $
sigil, and must not include package qualification. ESCAPE_VAR will be defined lexically, being visible in the code textually contained within the block. Its value will be a reference to an escape continuation targetting the block. Calling the continuation as a function will result in the code block exiting, returning the values that were passed to the continuation.
Do not assign a new value to ESCAPE_VAR. In this version of this module, ESCAPE_VAR behaves like a normal writable variable, but this is an implementation accident and may change in a future version.
- with_escape_function ESCAPE_VAR;
-
This form is a complete statement, ending with semicolon. ESCAPE_VAR is lexically defined within the enclosing block (from this statement to the end of the block), and contains a reference to an unblessed escape function for the enclosing block.
- with_escape_function ESCAPE_VAR BLOCK
-
This form is a complete statement, ending with the closing brace of BLOCK. BLOCK is executed normally. ESCAPE_VAR is lexically defined within BLOCK, and contains a reference to an unblessed escape function for the BLOCK.
- with_escape_function(ESCAPE_VAR BLOCK)
-
This form is an expression. BLOCK is executed normally, and its return value will become the value of this expression. ESCAPE_VAR is lexically defined within BLOCK, and contains a reference to an unblessed escape function for the BLOCK.
- with_escape_continuation ESCAPE_VAR;
-
This form is a complete statement, ending with semicolon. ESCAPE_VAR is lexically defined within the enclosing block (from this statement to the end of the block), and contains a reference to a blessed escape continuation for the enclosing block.
- with_escape_continuation ESCAPE_VAR BLOCK
-
This form is a complete statement, ending with the closing brace of BLOCK. BLOCK is executed normally. ESCAPE_VAR is lexically defined within BLOCK, and contains a reference to a blessed escape continuation for the BLOCK.
- with_escape_continuation(ESCAPE_VAR BLOCK)
-
This form is an expression. BLOCK is executed normally, and its return value will become the value of this expression. ESCAPE_VAR is lexically defined within BLOCK, and contains a reference to a blessed escape continuation for the BLOCK.
Returnable blocks with lexically-scoped names
This facility provides lexical scoping of names for non-locally returnable blocks, while avoiding visible reification of continuations. (with_escape_function
and with_escape_continuation
provide the same lexical scoping, but reify the continuations and put the lexical names in the ordinary scalar variable namespace.)
In each version, there is a code block which is labelled with a static bareword identifier TAG. The tag is lexically scoped, being visible in the code textually contained within the block. The return_from
operator can then be used to return from the textually enclosing block with a specified tag.
In Common Lisp (the model for these special forms), there are many implicit returnable blocks, in addition to the explicit ones established by the block
operator. Most notably, the body of each defun
-defined function is a returnable block tagged with the function's name. This module does not perceive any such implicit blocks: a return_from
form will only return from a block explicitly established with block
.
- block TAG;
-
This form is a complete statement, ending with semicolon. The enclosing block (from this statement to the end of the block) is returnable, tagged with TAG.
- block TAG BLOCK
-
This form is a complete statement, ending with the closing brace of BLOCK. BLOCK is executed normally. BLOCK is returnable, tagged with TAG.
- block(TAG BLOCK)
-
This form is an expression. BLOCK is executed normally, and its return value will become the value of this expression. BLOCK is returnable, tagged with TAG.
- return_from TAG VALUE ...
- return_from(TAG VALUE ...)
-
This form is an expression. It transfers control to exit from the lexically enclosing returnable block tagged with TAG. If there is no matching block, it is a compile-time error. Zero or more VALUEs may be supplied, which determine what will be returned from the block. (Each VALUE is stated as an expression, which is evaluated normally.)
The VALUEs are interpreted according to the syntactic context in which the target block was invoked. In void context, all the VALUEs are ignored. In scalar context, only the last VALUE is returned, or
undef
if no VALUEs were supplied. In list context, the full list of VALUEs is used unmodified. Note that this non-local context information does not directly influence the evaluation of the VALUE expresssions.On Perls prior to 5.13.8, due to limitations of the API available to add-on parsing code, the form without parentheses is only available when it is the first thing in a statement.
Catch blocks with dynamically-scoped names
This facility provides dynamic scoping of names for non-locally returnable blocks, while avoiding visible reification of continuations. The blocks can "catch" values that are "thrown" by lexically-remote code. (There is some resemblance here to throwing and catching of exceptions, but this is not an exception mechanism in itself.)
In each version, there is a code block which is labelled with a (possibly runtime-generated) string identifier TAG. In the catch
form, TAG must be stated as a double-quoted or single-quoted string syntax, possibly including variable interpolation. The tag is dynamically scoped, being visible during the execution of the block. The throw
function can then be used to return from (throw a value to) the dynamically enclosing block (the catch block) with a specified tag.
The Common Lisp catch
and throw
operators allow any object to be used as a catch tag, and the tags are compared for object identity. This allows code to generate a catch tag that is guaranteed to be unique, simply by using a newly-allocated cons cell or similar object that is not referenced from anywhere else. If that sort of semantic is required, it is best implemented by using the with_escape_function
operator and saving the continuation reference in a local
ised global variable. It is more usual for Common Lisp catch tags to be symbols, which idiomatically correspond to Perl strings, compared for string equality.
- catch TAG;
-
This form is a complete statement, ending with semicolon. The enclosing block (from this statement to the end of the block) is a catch block, tagged with TAG.
- catch TAG BLOCK
-
This form is a complete statement, ending with the closing brace of BLOCK. BLOCK is executed normally. BLOCK is a catch block, tagged with TAG.
It is unspecified whether TAG is evaluated inside or outside the block context. Do not rely on this aspect of its behaviour. (Historically it was inside, but outside makes more sense, so this may change in the future.)
- catch(TAG BLOCK)
-
This form is an expression. BLOCK is executed normally, and its return value will become the value of this expression. BLOCK is a catch block, tagged with TAG.
It is unspecified whether TAG is evaluated inside or outside the block context. Do not rely on this aspect of its behaviour. (Historically it was inside, but outside makes more sense, so this may change in the future.)
- throw(TAG, VALUE ...)
-
This is a function; all arguments are evaluated normally. It transfers control to exit from the dynamically enclosing catch block tagged with TAG. If there is no matching block, it is a runtime error. (Currently signalled by
die
, but this may change in the future.) Zero or more VALUEs may be supplied, which determine what will be returned from the catch block.The VALUEs are interpreted according to the syntactic context in which the target block was invoked. In void context, all the VALUEs are ignored. In scalar context, only the last VALUE is returned, or
undef
if no VALUEs were supplied. In list context, the full list of VALUEs is used unmodified. Note that this non-local context information does not directly influence the evaluation of the VALUE expresssions.
BUGS
The constructs that declare lexically-scoped variables do not generate the "masks earlier declaration" warnings that they should.
The lexical variable defined by with_escape_function
and with_escape_continuation
is writable. It really ought to be read-only.
The custom parsing code required for most of the operators is only invoked if the operator is invoked using an unqualified name. For example, referring to catch
as Scope::Escape::Sugar::catch
won't work. This limitation should be resolved if Devel::CallParser or something similar migrates into the core in a future version of Perl.
On Perls prior to 5.13.8, due to limitations of the API available to add-on parsing code, some of the operators are implemented by rewriting the source for the normal Perl parser to parse. This process risks unwanted interaction with other syntax-mutating modules, and is likely to break if the operators are imported under a non-standard name. The resulting failures are likely to be rather mystifying.
On Perls prior to 5.13.8, due to the aforementioned limitations of the API available to add-on parsing code, the version of return_from
without parentheses is only available when it is the first thing in a statement. Since a return_from
expression never returns locally, there is little reason for it to be a subexpression anyway.
SEE ALSO
AUTHOR
Andrew Main (Zefram) <zefram@fysh.org>
COPYRIGHT
Copyright (C) 2010, 2011 Andrew Main (Zefram) <zefram@fysh.org>
LICENSE
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.