Bindings and Lexical Scope in Ruby
Tags: ruby, programming, beginners
Published: 2019-01-08 14:18:48

---
title: Bindings and Lexical Scope in Ruby
description: In this edition of Ruby Magic, we’ll show how scope works in Ruby’s methods, closures. We’ll also take a peek at how ERB uses the binding object to render templates.
tags: ruby, programming, beginners
canonical_url: https://blog.appsignal.com/2019/01/08/ruby-magic-bindings-and-lexical-scope.html
published: true
cover_image: https://blog.appsignal.com/images/blog/2019-01/bindings.jpg?
---

Happy new year, and welcome back to Ruby Magic! In this winter episode, we'll dive into bindings and scopes. So put on your skis and follow us deep into the woods.

Last time, we looked at [closures in Ruby](https://blog.appsignal.com/2018/09/04/ruby-magic-closures-in-ruby-blocks-procs-and-lambdas.html) by comparing blocks, procs and lambdas. Aside from the differences between the three types, we touched on what defines a *closure*.

> A closure is a first-class function with an environment. The environment is a mapping to the variables that existed when the closure was created. The closure retains its access to these variables, even if they’re defined in another scope.

We've explored Ruby's equivalent to first-class functions, but we conveniently skipped over *environments*. In this episode, we’ll look at how that environment works for closures, classes and class instances by examining how Ruby handles *lexical scope* through its *bindings*.

## Lexical Scope

In programming, scope refers to the *bindings* available at a specific part of the code. A binding, or *name binding*, binds a name to a memory reference, like a variable's name to its value. The scope defines what `self` means, the methods that can be called, and the variables that are available.

Ruby, like most modern programming languages, uses a static scope, often called *lexical scope* (as opposed to [dynamic scope](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope_vs._dynamic_scope)). The current scope is based on the structure of the code and determines the variables available at specific parts of the code. This means that the scope changes when code jumps between methods, blocks and classes—as they can all have different local variables, for example.

```ruby
def bar
  foo = 1
  foo
end

bar #  => 1
```

In this method, we create a *local variable* inside a method and print it to the console. The variable is *in scope* inside the method, as it’s created there.

```ruby
foo = 1

def bar
  foo
end

bar # => NameError (undefined local variable or method `foo' for main:Object)
```

In this example, we create the variable outside the method. When we call the variable inside a method, we get an error, as the variable is *out of scope*. Local variables are tightly scoped, meaning a method can’t access a variable outside itself unless it’s passed as an argument.

```ruby
@foo = 1

def bar
  @foo
end

bar #  => 1
```

While local variables are available locally, *instance variables* are available to all methods of a class instance.

## Inherited Scopes and Proc Bindings

As we've seen in the previous examples, the scope is based on the location in the code. A local variable defined outside a method is not in scope inside the method but can be made available by turning it into an instance variable. Methods can't access local variables defined outside of them because methods have their own scope, with their own bindings.

Procs (including blocks and lambda's, by extension) are different. Whenever a proc is instantiated, a binding is created which inherits references to the local variables in the context the block was created.

``` ruby
foo = 1
Proc.new { foo }.call # => 1
```

In this example, we set a variable named `foo` to `1`. Internally, the Proc object created on the second line creates a new binding. When calling the proc, we can ask for the value of the variable.

Since the binding is created when the proc is initialized, we can’t create the proc before defining the variable, even if the block isn't called until after the variable is defined.

``` ruby
proc = Proc.new { foo }
foo = 1
proc.call # => NameError (undefined local variable or method `foo' for main:Object)
```

Calling the proc will produce a `NameError` as the variable isn’t defined in the proc’s bindings. Thus, any variables accessed in a proc should be defined before the proc is created or passed as an argument.

``` ruby
foo = 1
proc = Proc.new { foo }
foo = 2
proc.call # => 2
```

We can, however, change the variable after it has been defined in the main context since the proc’s binding holds a reference to it instead of copying it.

``` ruby
foo = 1
Proc.new { foo = 2 }.call
foo #=> 2
```

In this example, we can see that the `foo` variable points to the same object when in the proc as outside of it. We can update it inside the proc to have the variable outside of it updated as well.

## Bindings

To keep track of the current scope, Ruby uses *bindings*, which encapsulate the execution context at each position in the code. The `binding` method returns a `Binding` object which describes the bindings at the current position.

```ruby
foo = 1
binding.local_variables # => [:foo]
```

The binding object has a method named `#local_variables` which returns the names of all local variables available in the current scope.

```ruby
foo = 1
binding.eval("foo") # => 1
```

Code can be evaluated on the binding by using the `#eval` method. The example above isn't very useful, as simply calling `foo` would have the same result. However, since a binding is an object that can be passed around, it can be used for some more interesting things. Let's look at an example.

### A Real-Life Example

Now that we've learned about bindings in the safety of our garage, like take them out on to the slopes and play around in the snow. Aside from Ruby's internal use of bindings throughout the language, there are some situations where binding objects are used explicitly. A good example is ERB—Ruby's templating system.

```ruby
require 'erb'

x = 1

def y
  2
end

template = ERB.new("x is <%= x %>, y() returns <%= y %>, self is `<%= self %>`")
template.result(binding) # => "x is 1, y() returns 2, self is `main`"
```

In this example, we create a variable named `x`, a method called `y`, and an ERB template that references both. We then pass the current binding to `ERB#result`, which evaluates the ERB tags in the template and returns a string with the variables filled in.

Under the hood, ERB uses `Binding#eval` to evaluate each ERB tag's contents in the scope of the passed binding. A simplified implementation that works for the example above could look like this:

```ruby
class DiyErb
  def initialize(template)
    @template = template
  end

  def result(binding)
    @template.gsub(/<%=(.+?)%>/) do
      binding.eval($1)
    end
  end
end

x = 1

def y
  2
end

template = DiyErb.new("x is <%= x %>, y() returns <%= y %>, self is `<%= self %>`")
template.result(binding) # => "x is 1, y() returns 2, self is `main`"
```

The `DiyErb` class takes a template string on initialization. Its `#result` method finds all ERB tags and replaces them with the result of evaluating their contents. To do that, it calls `Binding#eval` on the passed binding, with the contents of the ERB tags.

By passing the current binding when calling the `#result` method, the `eval` calls can access the variables defined outside of the method, and even outside of the class, without having to pass them explicitly.

## Did we lose you in the woods?

We hope you enjoyed our ski trip into the woods. We went deeper into scopes and closures, after glossing over them. We hope we haven't lost you in the woods. Please let us know if you'd like to learn more about bindings, or have any other Ruby topic you'd like to dive into.

Thanks for following us and please kick the snow off your bindings before leaving them for the next developer. If you like these magical trips, you might like to [subscribe to Ruby Magic](https://blog.appsignal.com/ruby-magic) to receive an e-mail when we publish a new article about once a month.