Issue #12901 has been updated by Jrg W Mittag.


I have thought about this a number of times, but never got around to writing a feature request: we already have a way to tell Ruby that we don't want to capture certain variables from the surrounding scope: block local variables. We also have "wildcard" syntax for block parameters. So, let's just combine the two, to tell Ruby that we don't want to capture any variables:

    Proc.new {|a, b:, *c; d, e, *| }
   # or
    -> (a, b:, *c; d, e, *) {}

This will make `d` and `e` block local variables, and make the block not close over the surrounding environment.

This is a conservative extension of existing syntax.

We can even further extend it to provide a "whitelist" of variables to capture:

    Proc.new {|a, b:, *c; d, e, *; f, g| }
   # or
    -> (a, b:, *c; d, e, *; f, g) {}

This will make it an error if there are no local variables named `f` and `g` in the surrounding scope. The *current* behavior would then be equivalent to:

    Proc.new {|a, b:, *c;; *| }
   # or
    -> (a, b:, *c;; *) {}

I.e.: no block local variables, capture all variables.

We will need to make a decision, though, as to whether this should include lexical binding of `self` or not. We could easily allow both explicit opt-out and explicit opt-in by allowing `self` as a legal identifier in the parameter list. E.g. a block that captures no variables, but *does* capture `self` would be declared like:

    Proc.new {|a, b:, *c;* ; self| }
   # or
    -> (a, b:, *c; *; self) {}

A block that captures all variables but does not capture `self`:

    Proc.new {|a, b:, *c;self ; *| }
   # or
    -> (a, b:, *c; self; *) {}

and a block that captures *nothing*:

    Proc.new {|a, b:, *c;*, self| }
   # or
    -> (a, b:, *c; *, self) {}

An open question is: what should the default for capturing `self` be, when there is only a wildcard, i.e. these two cases:

    Proc.new {|a, b:, *c;; *| }
   # or
    -> (a, b:, *c;; *) {}

    Proc.new {|a, b:, *c; *| }
   # or
    -> (a, b:, *c; *) {}

I have no preference either way.

----------------------------------------
Feature #12901: Anonymous functions without scope lookup overhead
https://bugs.ruby-lang.org/issues/12901#change-62674

* Author: Richard Schneeman
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
Right now if you are writing performance sensitive code you can get a performance bump by not using anonymous functions (procs, lambdas, blocks) due to the scope lookup overhead.

https://twitter.com/apotonick/status/791167782532493312?lang=en

I would like to be able to create an anonymous function and specify I don't need it to perform the lookup when called.

I am thinking that this code:

~~~
Proc.new(scope: false) {|var| puts var }
~~~

Would be the equivalent of 

~~~
def anon(var)
  puts var
end
~~~

If you call it while trying to access something from outside of the scope it would error

~~~
var = "hello"
Proc.new(scope: false) { puts var }.call
# => NameError: undefined local variable or method `var' for main:Object
~~~

An example of a case where this could be used is found in https://github.com/rails/sprockets/pull/417. In this case we are getting rid of anonymous functions in favor of a method created with refinements. This solution works but it was not an obvious optimization. It would be convenient to have a syntax for defining anonymous functions that do not need access to caller scope.






-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>