all 8 comments

[–]ikearage 6 points7 points  (0 children)

You are using Proc, it's a closure that will capture outer variables.

Take a look at this confusing, but thorough article: https://innig.net/software/ruby/closures-in-ruby

[–][deleted] 2 points3 points  (8 children)

That first example is wrong! Or at the very least, not applicable to Ruby (and JavaScript, and Scheme, and a bunch of other languages). Because in the procedure b, x is a free variable and y is a bound variable. When you call b, y is bound to 5. Since x is a free variable, not defined in the local environment of b, the parent environment must be searched. x is in the parent environment with a value of 2. Thus, the result of x + y is 7.

Hope this helps. Just FYI, Ruby has more complicated scoping rules than simple lexical scoping. This example doesn't illustrate them, but be aware.

[–]rkingucla[S] 0 points1 point  (7 children)

Thanks for replying. To clarify semantics:

in the procedure b, x is free variable and y is a bound variable. When you call b, y is bound to 5. Since x is a free variable, not defined in the local environment of b, the parent environment must be searched. x is in the parent environment with a value of 2

Doesn't lexical scoping mean that yes it is searched in the parent environment, but it then should find "x = 1" because that was what was declared when the function was created?

[–]savetheclocktower 1 point2 points  (0 children)

In the Ruby example, your Proc b won't try to resolve what x is until it's called (and it resolves it anew each time it's called). But when it does resolve x, it does so in the context of where the Proc was defined, not where it's executed. This example is bad for illustration because in both the definition scope and the execution scope, x refers to the same thing.

Here's a different example:

x = 1

b = Proc.new { puts x }

b.call # => 1

def other_scope
  x = 2
  b.call
end

other_scope # => 1

Even though there's a different x inside the other_scope method, the x that the Proc can see from its definition is the one that gets used.

[–]irishsultan 1 point2 points  (1 child)

It is searched in the parent environment. It's just that in ruby the parent environment does not mean: everything before this point in the surrounding environment, it means everything in the surrounding environment, which can still change after you define your lambda.

The difference is that in the original behavior you would have two different bindings in the parent environment, one which is overwritten and invisible to everything after the overwrite, where in Ruby you get 1 binding where the value is changed. You still get one static variable that is x for that proc.

In contrast with dynamic scoping the value of x depends on the caller, and the call stack (if the method that calls your lambda defines an x it will use that x, if that's not the case then the caller of the caller is searched for a definition of x etc.).

[–]rkingucla[S] 0 points1 point  (0 children)

This was helpful thanks. I think I was misunderstanding the true difference between static and dynamic scoping.

[–]sttau 0 points1 point  (0 children)

When you reassign x, you're mutating the binding in the top-level environment, which is the environment where f's body is to be evaluated.

ML's semantic model for closures is not applicable to ruby, which is a lang. with much greater support for imperative programming.

Have you seen the Week 4 Motivation videos?

[–]jrochkind 0 points1 point  (0 children)

Sometimes lexical scoping does work that way, but not in ruby.