summaryrefslogtreecommitdiff
path: root/proc.c
diff options
context:
space:
mode:
authorTanaka Akira <akr@fsij.org>2019-07-14 15:01:39 +0900
committerTanaka Akira <akr@fsij.org>2019-07-14 15:01:39 +0900
commit357f295a0e97a105e51d0b3a4b5c062c55dffe92 (patch)
treef3513a60c22d29df101c3e7beedb84be98eed394 /proc.c
parentdda2c860d97ce3149361b143c9ccc5ed1a2f1af2 (diff)
Describe lambda-ness of Proc more.
Diffstat (limited to 'proc.c')
-rw-r--r--proc.c69
1 files changed, 65 insertions, 4 deletions
diff --git a/proc.c b/proc.c
index 1a3f337295..7f4f2d46b6 100644
--- a/proc.c
+++ b/proc.c
@@ -172,8 +172,10 @@ proc_clone(VALUE self)
* call-seq:
* prc.lambda? -> true or false
*
- * Returns +true+ for a Proc object for which argument handling is rigid.
- * Such procs are typically generated by +lambda+.
+ * Returns +true+ if a Proc object is lambda.
+ * +false+ if non-lambda.
+ *
+ * The lambda-ness affects argument handling and the behavior of +return+ and +break+.
*
* A Proc object generated by +proc+ ignores extra arguments.
*
@@ -3370,9 +3372,11 @@ rb_method_compose_to_right(VALUE self, VALUE g)
* Procs are coming in two flavors: lambda and non-lambda (regular procs).
* Differences are:
*
- * * In lambdas, +return+ means exit from this lambda;
- * * In regular procs, +return+ means exit from embracing method
+ * * In lambdas, +return+ and +break+ means exit from this lambda;
+ * * In non-lambda procs, +return+ means exit from embracing method
* (and will throw +LocalJumpError+ if invoked outside the method);
+ * * In non-lambda procs, +break+ means exit from the method which the block given for.
+ * (and will throw +LocalJumpError+ if invoked after the method returns);
* * In lambdas, arguments are treated in the same way as in methods: strict,
* with +ArgumentError+ for mismatching argument number,
* and no additional argument processing;
@@ -3383,6 +3387,40 @@ rb_method_compose_to_right(VALUE self, VALUE g)
*
* Examples:
*
+ * # +return+ in non-lambda proc, +b+, exits +m2+.
+ * # (The block +{ return }+ is given for +m1+ and embraced by +m2+.)
+ * $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1 { return }; $a << :m2 end; m2; p $a
+ * #=> []
+ *
+ * # +break+ in non-lambda proc, +b+, exits +m1+.
+ * # (The block +{ break }+ is given for +m1+ and embraced by +m2+.)
+ * $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1 { break }; $a << :m2 end; m2; p $a
+ * #=> [:m2]
+ *
+ * # +next+ in non-lambda proc, +b+, exits the block.
+ * # (The block +{ next }+ is given for +m1+ and embraced by +m2+.)
+ * $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1 { next }; $a << :m2 end; m2; p $a
+ * #=> [:m1, :m2]
+ *
+ * # Using +proc+ method changes the behavior as follows because
+ * # The block is given for +proc+ method and embraced by +m2+.
+ * $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&proc { return }); $a << :m2 end; m2; p $a
+ * #=> []
+ * $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&proc { break }); $a << :m2 end; m2; p $a
+ * # break from proc-closure (LocalJumpError)
+ * $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&proc { next }); $a << :m2 end; m2; p $a
+ * #=> [:m1, :m2]
+ *
+ * # +return+, +break+ and +next+ in the subby lambda exits the block.
+ * # (+lambda+ method behaves same.)
+ * # (The block is given for stubby lambda syntax and embraced by +m2+.)
+ * $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&-> { return }); $a << :m2 end; m2; p $a
+ * #=> [:m1, :m2]
+ * $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&-> { break }); $a << :m2 end; m2; p $a
+ * #=> [:m1, :m2]
+ * $a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&-> { next }); $a << :m2 end; m2; p $a
+ * #=> [:m1, :m2]
+ *
* p = proc {|x, y| "x=#{x}, y=#{y}" }
* p.call(1, 2) #=> "x=1, y=2"
* p.call([1, 2]) #=> "x=1, y=2", array deconstructed
@@ -3487,6 +3525,29 @@ rb_method_compose_to_right(VALUE self, VALUE g)
* {test: 1}.to_proc.call(:test) #=> 1
* %i[test many keys].map(&{test: 1}) #=> [1, nil, nil]
*
+ * == Orphaned Proc
+ *
+ * +return+ and +break+ in a block exit a method.
+ * If a Proc object is generated from the block and the Proc object
+ * survives until the method is returned, +return+ and +break+ cannot work.
+ * In such case, +return+ and +break+ raises LocalJumpError.
+ * A Proc object in such situation is called as orphaned Proc object.
+ *
+ * Note that the method to exit is different for +return+ and +break+.
+ * There is a situation that orphaned for +break+ but not orphaned for +return+.
+ *
+ * def m1(&b) b.call end; def m2(); m1 { return } end; m2 # ok
+ * def m1(&b) b.call end; def m2(); m1 { break } end; m2 # ok
+ *
+ * def m1(&b) b end; def m2(); m1 { return }.call end; m2 # ok
+ * def m1(&b) b end; def m2(); m1 { break }.call end; m2 # LocalJumpError
+ *
+ * def m1(&b) b end; def m2(); m1 { return } end; m2.call # LocalJumpError
+ * def m1(&b) b end; def m2(); m1 { break } end; m2.call # LocalJumpError
+ *
+ * Since +return+ and +break+ exists the block itself in lambdas,
+ * lambdas cannot be orphaned.
+ *
*/