Skip to content
By Victor Hazbun

Using Ruby Closures to simulate classes

A technique commonly used in JavaScript is to use closures in your objects instead of classes. This is done to ensure that variables do not leak out and inadvertently become overridden by some other function or operation.

This technique can also be used in Ruby. The scoping rules of Ruby are such that when a lambda is defined, that lambda also has access to all the variables that are in scope.

Let's say you want to build a simple counter program. This counter program will do the following:

  • Get the current value of the counter.
  • Increment the counter.
  • Decrement the counter.

This is the essence of what most classes do: retrieve and modify data. Here is one possible implementation of a Counter class:

Class Counter

    def initialize
        @x = 0
    end

    def get_x
        @x  
    end

    def increment
        @x += 1
    end

    def decrement
        @x -= 1 
    end
end

# Here is a sample run in irb:

    >> c = Counter.new
    => #<Counter:0x007f9335939848 @x=0>
    >> c.increment
    => 1
    >> c.increment
    => 2
    >> c.get_x
    => 2
    >> c.decrement
    => 1
    >> c.decrement
    => 0
    >> c.decrement
    => -1

There should not be anything surprising with this example. Let's add some constraints.

Imagine if you didn't have the ability to create classes. Could you still write a counter program? With lambdas, you most definitely can.

Create a new file called lambda_counter.rb with the following code:

Counter = lambda do # 1 

    # x = 0 # 2

    get_x = lambda { x } # 3

    increment = lambda { x += 1 } # 4

    decrement = lambda { x -= 1} # 5

    {get_x: get_x, increment: increment, decrement: decrement} # 6
end # 8

Here, Counter is a lambda. Line 2 declares x, the state of the counter, and initialises it to zero. Line 3 creates a lambda that returns the current state of the counter. Lines 4 and 5 both modify the state of the counter, by increasing or decreasing the value of x respectively.

It should be apparent to you now that x is the free variable. Finally, on line 6, the return result of the most outermost lambda is a hash whose keys are the names of the respective lambdas.

By saving the return values, you can get a reference to the respective lambdas and manipulate the counter. And manipulate the counter you will!

Load the lambda_counter.rb file in irb:

% irb -r ./lambda_counter.rb

>> c1 = Counter.call
=> {:get_x=>#<Proc:0x007fa92904ea28@/counter.rb:4 (lambda)>,
:incr=>#<Proc:0x007fa92904e910@/counter.rb:6 (lambda)>,
:decr=>#<Proc:0x007fa92904e898@/counter.rb:8 (lambda)>}`

Counter c1 is a hash where each key points to a Proc. Let’s perform some operations on the counter:

>> c1[:incr].call
=> 1
>> c1[:incr].call
=> 2
>> c1[:incr].call
=> 3
>> c1[:decr].call
=> 2
>> c1[:get_x].call
=> 2

Let’s create another counter, c2. Is c2 distinct from c1? In other words, do they behave like distinct objects?

>> c2 = Counter.call
=> {:get_x=>#<Proc:0x007fa92a1fcc98@/counter.rb:4 (lambda)>,
:incr=>#<Proc:0x007fa92a1fcc70@/counter.rb:6 (lambda)>,
:decr=>#<Proc:0x007fa92a1fcc48@/counter.rb:8 (lambda)>}
>> c2[:get_x].call
=> 0
>> c1[:get_x].call
=> 2

Both c1 and c2 get their own x.

Conclusion

So there you have it: it is entirely possible to have objects without classes. While you probably wouldn’t want to use this technique in your day-to-day Ruby programming, there’s an important lesson to be drawn here.

As the counter example has shown, closures restrict access to the variables they wrap.

Latest Articles by Our Team

Our expert team of designers and developers love what the do and enjoy sharing their knowledge with the world.

We Hire Only the Best

reinteractive is Australia’s largest dedicated Ruby on Rails development company. We don’t cut corners and we know what we are doing.

We are an organisation made up of amazing individuals and we take pride in our team. We are 100% remote work enabling us to choose the best talent no matter which part of the country they live in. reinteractive is dedicated to making it a great place for any developer to work.

Free Community Workshops

We created the Ruby on Rails InstallFest and Ruby on Rails Development Hub to help introduce new people to software development and to help existing developers hone their skills. These workshops provide invaluable mentorship to train developers, addressing key skills shortages in the industry. Software development is a great career choice for all ages and these events help you get started and skilled up.

  • Webinars

    Webinars

    Webinars are our online portal for tips, tricks and lessons learned in everything we do. Make the most of this free resource to help you become a better developer.

    Learn more about webinars

  • Installfest

    Installfest

    The Ruby on Rails Installfest includes a full setup of your development environment and step-by-step instructions on how to build your first app hosted on Heroku. Over 1,800 attendees to date and counting.

    Learn more about Installfest

  • Development Hub

    Development Hub

    The Ruby on Rails Development Hub is a monthly event where you will get the chance to spend time with our team and others in the community to improve and hone your Ruby on Rails skills.

    Learn more about Development Hub

Get the “reinteractive Review” Monthly Email