Ruby, managing global variables, and dynamic scope

Everyone knows that global variables are bad. However, they are quite unavoidable. Java’s System.out is an example.

Globals, sometimes offer a certain kind of flexibility. They offer a way to tweak the behavior of the entire (or a subset of) the system. For instance, one can redirect System.out to a different stream (into a file, say) and capture all the messages a program spits out.

This kind of stuff works nicely. Except when someone else goes and changes the same variable to something else in another part of the code, and you’ve no idea where.

What is needed, is something like optional dynamic scope, so that the globals can be used when needed, but can be managed better. After all, from the above example, what seems to be needed is a way for a piece of code to say – for my purposes, and for all code that runs when I’m called, I want the value of this global(s) to be _something_, and when I return, these globals should be reset.

This can be done manually, by saving the existing value of a global before setting it to something else, and then resetting it back when the code block completes running. Perl and Common Lisp have had a mechanism to do this type of stuff for a long time, built into the language.

Here’s a hacky (and probably naive) way to implement this in Ruby, to illustrate how this might work –


module Let
def let(bindings, &block)
old_bindings = capture_existing_bindings_for(bindings)
block.call
rehydrate_old_bindings_with(old_bindings)
end
def capture_existing_bindings_for(bindings)
old_bindings = { }
bindings.each do |k, v|
old_bindings[k] = eval "@"+k.to_s
create_binding k, v
end
return old_bindings
end
def create_binding(var_name, value)
instance_eval "@#{var_name}=value"
end
def rehydrate_old_bindings_with(old_bindings)
old_bindings.each do |k, v|
create_binding k, v
end
end
end

And the way you would use this, would look something like this –


require 'let_module'
include Let
@num_var = 1
@char_var = 'a'
class Car
attr_reader :name
def initialize(name)
@name = name
end
end
@obj_var = Car.new("hyundai")
def do_something
puts "num_var is " + @num_var.to_s + ", char_var is '" + @char_var.to_s + "', obj_var is " + @obj_var.name
puts "returning"
end
do_something
puts "changing num_var to 2, char_var to b, obj_var to 'kia'"
let :num_var => 2, :char_var => 'b', :obj_var => Car.new("kia") do
do_something
puts "changing num_var to 3, char_var to c, obj_var to 'toyota'"
let :num_var => 3, :char_var => 'c', :obj_var => Car.new("toyota") do
do_something
end
do_something
end
do_something

And when run, would produce this –

num_var is 1, char_var is 'a', obj_var is hyundai
returning
changing num_var to 2, char_var to b, obj_var to 'kia'
num_var is 2, char_var is 'b', obj_var is kia
returning
changing num_var to 3, char_var to c, obj_var to 'toyota'
num_var is 3, char_var is 'c', obj_var is toyota
returning
num_var is 2, char_var is 'b', obj_var is kia
returning
num_var is 1, char_var is 'a', obj_var is hyundai
returning

This could be one way to control unruly global variables in Ruby.

One thought on “Ruby, managing global variables, and dynamic scope

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s