]> git.vpit.fr Git - perl/modules/Scope-Upper.git/blobdiff - README
This is 0.18
[perl/modules/Scope-Upper.git] / README
diff --git a/README b/README
index 7b6405aea649135bfa3912d58687dc3bf3e3fb38..7d441b7558fa36830cac4af0e6297130b6b95c27 100644 (file)
--- a/README
+++ b/README
@@ -2,14 +2,17 @@ NAME
     Scope::Upper - Act on upper scopes.
 
 VERSION
-    Version 0.13
+    Version 0.18
 
 SYNOPSIS
     "reap", "localize", "localize_elem", "localize_delete" and "WORDS" :
 
         package Scope;
 
-        use Scope::Upper qw/reap localize localize_elem localize_delete :words/;
+        use Scope::Upper qw<
+         reap localize localize_elem localize_delete
+         :words
+        >;
 
         sub new {
          my ($class, $name) = @_;
@@ -51,28 +54,28 @@ SYNOPSIS
         package UserLand;
 
         {
-         Scope->new("top");      # initializes $UserLand::tag
+         Scope->new("top");    # initializes $UserLand::tag
 
          {
           Scope->catch;
-          my $one = 1 + undef;   # prints "top: Use of uninitialized value..."
+          my $one = 1 + undef; # prints "top: Use of uninitialized value..."
 
           {
            Scope->private;
            eval { require Cwd };
-           print $@;             # prints "Can't locate Cwd.pm in @INC (@INC contains:) at..."
-          }
+           print $@;           # prints "Can't locate Cwd.pm in @INC
+          }                    #         (@INC contains:) at..."
 
-          require Cwd;           # loads Cwd.pm
+          require Cwd;         # loads Cwd.pm
          }
 
-        }                        # prints "top: done"
+        }                      # prints "top: done"
 
     "unwind" and "want_at" :
 
         package Try;
 
-        use Scope::Upper qw/unwind want_at :words/;
+        use Scope::Upper qw<unwind want_at :words>;
 
         sub try (&) {
          my @result = shift->();
@@ -84,16 +87,57 @@ SYNOPSIS
 
         sub zap {
          try {
-          my @things = qw/a b c/;
+          my @things = qw<a b c>;
           return @things; # returns to try() and then outside zap()
           # not reached
          };
          # not reached
         }
 
-        my @stuff = zap(); # @stuff contains qw/a b c/
+        my @stuff = zap(); # @stuff contains qw<a b c>
         my $stuff = zap(); # $stuff contains 3
 
+    "uplevel" :
+
+        package Uplevel;
+
+        use Scope::Upper qw<uplevel CALLER>;
+
+        sub target {
+         faker(@_);
+        }
+
+        sub faker {
+         uplevel {
+          my $sub = (caller 0)[3];
+          print "$_[0] from $sub()";
+         } @_ => CALLER(1);
+        }
+
+        target('hello'); # "hello from Uplevel::target()"
+
+    "uid" and "validate_uid" :
+
+        use Scope::Upper qw<uid validate_uid>;
+
+        my $uid;
+
+        {
+         $uid = uid();
+         {
+          if ($uid eq uid(UP)) { # yes
+           ...
+          }
+          if (validate_uid($uid)) { # yes
+           ...
+          }
+         }
+        }
+
+        if (validate_uid($uid)) { # no
+         ...
+        }
+
 DESCRIPTION
     This module lets you defer actions *at run-time* that will take place
     when the control flow returns into an upper scope. Currently, you can:
@@ -105,7 +149,12 @@ DESCRIPTION
         "localize_delete" ;
 
     *   return values immediately to an upper level with "unwind", and know
-        which context was in use then with "want_at".
+        which context was in use then with "want_at" ;
+
+    *   execute a subroutine in the setting of an upper subroutine stack
+        frame with "uplevel" ;
+
+    *   uniquely identify contextes with "uid" and "validate_uid".
 
 FUNCTIONS
     In all those functions, $context refers to the target scope.
@@ -234,6 +283,147 @@ FUNCTIONS
 
     will rightfully set $num to 26.
 
+  "uplevel $code, @args, $context"
+    Executes the code reference $code with arguments @args as if it were
+    located at the subroutine stack frame pointed by $context, effectively
+    fooling "caller" and "die" into believing that the call actually
+    happened higher in the stack. The code is executed in the context of the
+    "uplevel" call, and what it returns is returned as-is by "uplevel".
+
+        sub target {
+         faker(@_);
+        }
+
+        sub faker {
+         uplevel {
+          map { 1 / $_ } @_;
+         } @_ => CALLER(1);
+        }
+
+        my @inverses = target(1, 2, 4); # @inverses contains (0, 0.5, 0.25)
+        my $count    = target(1, 2, 4); # $count is 3
+
+    Sub::Uplevel also implements a pure-Perl version of "uplevel". Both are
+    identical, with the following caveats :
+
+    *   The Sub::Uplevel implementation of "uplevel" may execute a code
+        reference in the context of any upper stack frame. The Scope::Upper
+        version can only uplevel to a subroutine stack frame, and will croak
+        if you try to target an "eval" or a format.
+
+    *   Exceptions thrown from the code called by this version of "uplevel"
+        will not be caught by "eval" blocks between the target frame and the
+        uplevel call, while they will for Sub::Uplevel's version. This means
+        that :
+
+            eval {
+             sub {
+              local $@;
+              eval {
+               sub {
+                uplevel { die 'wut' } CALLER(2); # for Scope::Upper
+                # uplevel(3, sub { die 'wut' })  # for Sub::Uplevel
+               }->();
+              };
+              print "inner block: $@";
+              $@ and exit;
+             }->();
+            };
+            print "outer block: $@";
+
+        will print "inner block: wut..." with Sub::Uplevel and "outer block:
+        wut..." with Scope::Upper.
+
+    *   Sub::Uplevel globally overrides the Perl keyword "caller", while
+        Scope::Upper does not.
+
+    A simple wrapper lets you mimic the interface of "uplevel" in
+    Sub::Uplevel :
+
+        use Scope::Upper;
+
+        sub uplevel {
+         my $frame = shift;
+         my $code  = shift;
+         my $cxt   = Scope::Upper::CALLER($frame);
+         &Scope::Upper::uplevel($code => @_ => $cxt);
+        }
+
+    Albeit the three exceptions listed above, it passes all the tests of
+    Sub::Uplevel.
+
+  "uid $context"
+    Returns an unique identifier (UID) for the context (or dynamic scope)
+    pointed by $context, or for the current context if $context is omitted.
+    This UID will only be valid for the life time of the context it
+    represents, and another UID will be generated next time the same scope
+    is executed.
+
+        my $uid;
+
+        {
+         $uid = uid;
+         if ($uid eq uid()) { # yes, this is the same context
+          ...
+         }
+         {
+          if ($uid eq uid()) { # no, we are one scope below
+           ...
+          }
+          if ($uid eq uid(UP)) { # yes, UP points to the same scope as $uid
+           ...
+          }
+         }
+        }
+
+        # $uid is now invalid
+
+        {
+         if ($uid eq uid()) { # no, this is another block
+          ...
+         }
+        }
+
+    For example, each loop iteration gets its own UID :
+
+        my %uids;
+
+        for (1 .. 5) {
+         my $uid = uid;
+         $uids{$uid} = $_;
+        }
+
+        # %uids has 5 entries
+
+    The UIDs are not guaranteed to be numbers, so you must use the "eq"
+    operator to compare them.
+
+    To check whether a given UID is valid, you can use the "validate_uid"
+    function.
+
+  "validate_uid $uid"
+    Returns true if and only if $uid is the UID of a currently valid context
+    (that is, it designates a scope that is higher than the current one in
+    the call stack).
+
+        my $uid;
+
+        {
+         $uid = uid();
+         if (validate_uid($uid)) { # yes
+          ...
+         }
+         {
+          if (validate_uid($uid)) { # yes
+           ...
+          }
+         }
+        }
+
+        if (validate_uid($uid)) { # no
+         ...
+        }
+
 CONSTANTS
   "SU_THREADSAFE"
     True iff the module could have been built when thread-safety features.
@@ -284,13 +474,13 @@ WORDS
            {
             reap \&cleanup => $cxt;
             ...
-           }     # $cxt = SCOPE(0), or HERE
+           }     # $cxt = SCOPE(0) = HERE
            ...
-          }->(); # $cxt = SCOPE(1), or UP, or SUB, or CALLER, or CALLER(0)
+          }->(); # $cxt = SCOPE(1) = UP = SUB = CALLER(0)
           ...
-         };      # $cxt = SCOPE(2), or UP UP, or UP SUB, or EVAL, or CALLER(1)
+         };      # $cxt = SCOPE(2) = UP UP =  UP SUB = EVAL = CALLER(1)
          ...
-        }->();   # $cxt = SCOPE(3), or SUB UP SUB, or SUB EVAL, or CALLER(2)
+        }->();   # $cxt = SCOPE(3) = SUB UP SUB = SUB EVAL = CALLER(2)
         ...
 
     Where "localize", "localize_elem" and "localize_delete" act depending on
@@ -301,42 +491,45 @@ WORDS
           sub {
            {
             localize '$x' => 1 => $cxt;
-            # $cxt = SCOPE(0), or HERE
+            # $cxt = SCOPE(0) = HERE
             ...
            }
-           # $cxt = SCOPE(1), or UP, or SUB, or CALLER, or CALLER(0)
+           # $cxt = SCOPE(1) = UP = SUB = CALLER(0)
            ...
           }->();
-          # $cxt = SCOPE(2), or UP UP, or UP SUB, or EVAL, or CALLER(1)
+          # $cxt = SCOPE(2) = UP UP = UP SUB = EVAL = CALLER(1)
           ...
          };
-         # $cxt = SCOPE(3), or SUB UP SUB, or SUB EVAL, or CALLER(2)
+         # $cxt = SCOPE(3) = SUB UP SUB = SUB EVAL = CALLER(2)
          ...
         }->();
-        # $cxt = SCOPE(4), UP SUB UP SUB, or UP SUB EVAL, or UP CALLER(2), or TOP
+        # $cxt = SCOPE(4), UP SUB UP SUB = UP SUB EVAL = UP CALLER(2) = TOP
         ...
 
-    Where "unwind" and "want_at" point to depending on the $cxt:
+    Where "unwind", "want_at" and "uplevel" point to depending on the $cxt:
 
         sub {
          eval {
           sub {
            {
-            unwind @things => $cxt;
+            unwind @things => $cxt;   # or uplevel { ... } $cxt;
             ...
            }
            ...
-          }->(); # $cxt = SCOPE(0 .. 1), or HERE, or UP, or SUB, or CALLER(0)
+          }->(); # $cxt = SCOPE(0) = SCOPE(1) = HERE = UP = SUB = CALLER(0)
           ...
-         };      # $cxt = SCOPE(2), or UP UP, or UP SUB, or EVAL, or CALLER(1)
+         };      # $cxt = SCOPE(2) = UP UP = UP SUB = EVAL = CALLER(1) (*)
          ...
-        }->();   # $cxt = SCOPE(3), or SUB UP SUB, or SUB EVAL, or CALLER(2)
+        }->();   # $cxt = SCOPE(3) = SUB UP SUB = SUB EVAL = CALLER(2)
         ...
 
+        # (*) Note that uplevel() will croak if you pass that scope frame,
+        #     because it cannot target eval scopes.
+
 EXPORT
     The functions "reap", "localize", "localize_elem", "localize_delete",
-    "unwind" and "want_at" are only exported on request, either individually
-    or by the tags ':funcs' and ':all'.
+    "unwind", "want_at" and "uplevel" are only exported on request, either
+    individually or by the tags ':funcs' and ':all'.
 
     The constant "SU_THREADSAFE" is also only exported on request,
     individually or by the tags ':consts' and ':all'.
@@ -378,6 +571,28 @@ CAVEATS
     may help to use a perl higher than 5.8.9 or 5.10.0, as they contain some
     context-related fixes.
 
+    Calling "goto" to replace an "uplevel"'d code frame does not work :
+
+    *   for a "perl" older than the 5.8 series ;
+
+    *   for a "DEBUGGING" "perl" run with debugging flags set (as in "perl
+        -D ...") ;
+
+    *   when the runloop callback is replaced by another module.
+
+    In those three cases, "uplevel" will look for a "goto &sub" statement in
+    its callback and, if there is one, throw an exception before executing
+    the code.
+
+    Moreover, in order to handle "goto" statements properly, "uplevel"
+    currently has to suffer a run-time overhead proportional to the size of
+    the the callback in every case (with a small ratio), and proportional to
+    the size of all the code executed as the result of the "uplevel" call
+    (including subroutine calls inside the callback) when a "goto" statement
+    is found in the "uplevel" callback. Despite this shortcoming, this XS
+    version of "uplevel" should still run way faster than the pure-Perl
+    version from Sub::Uplevel.
+
 DEPENDENCIES
     XSLoader (standard since perl 5.006).
 
@@ -386,6 +601,8 @@ SEE ALSO
 
     Alias, Hook::Scope, Scope::Guard, Guard.
 
+    Sub::Uplevel.
+
     Continuation::Escape is a thin wrapper around Scope::Upper that gives
     you a continuation passing style interface to "unwind". It's easier to
     use, but it requires you to have control over the scope where you want
@@ -419,7 +636,7 @@ ACKNOWLEDGEMENTS
     Thanks to Shawn M. Moore for motivation.
 
 COPYRIGHT & LICENSE
-    Copyright 2008,2009,2010 Vincent Pit, all rights reserved.
+    Copyright 2008,2009,2010,2011 Vincent Pit, all rights reserved.
 
     This program is free software; you can redistribute it and/or modify it
     under the same terms as Perl itself.