Many Lisps provide dynamically scoped or special variables additionally to lexically scoped ones. Some (for example Elisp or ye olde MacLisp) even soley provide dynamically scoped variables. In fact, Scheme was the first lisp-based language to make lexical scoping popular.
Now, what’s the difference between dynamic and lexical scoping? Lexical scoping should be known to every Rubyist, as this is the usual (and the only one provided by default) way of scoping. Lexical scoping has the major advantage of enabling closures, that is, pieces of code that save their lexical environment. For example, in Ruby you can write:
def adder(n)
v = 0 # v is lexically scoped here
lambda { v += n } # v and n are accessed from the closure
end
add1 = adder(10)
p add1.call #=> 10
p add1.call #=> 20
p add1.call #=> 30
add2 = adder(5)
p add2.call #=> 5
p add2.call #=> 10
p add2.call #=> 15
But you already knew that. Not so with dynamic scope, and that’s why dynamic scope is usually avoided. However, there is one important and useful usage for dynamic scope: Providing contexts. For example, let’s say you write a currency converter in Elisp:
(defvar +eur2usd-factor+ 1.3068) ; 24apr2005
(defun eur2usd (euro)
(* +eur2usd-factor+ euro))
(eur2usd 10) ; => 13.068
(eur2usd 0.77) ; => 1.006236
Ok, that was easy. But not, let’s say we would like to know what our Euros would have been worth last year. Of course, we could globally change the factor, but that would be icky and we would need to be careful to change it back, so rather let’s dynamically rebind the value:
(let ((+eur2usd-factor+ 0.9267)) ; last year, maybe
(eur2usd 10) ; => 9.267
(eur2usd 0.77)) ; => 0.713559
This may surprise you, but eur2usd
looks up the dynamic value of
+eur2usd-factor+
, not the lexical value (which would have been
1.3068). let
redefines the value during the execution of his body,
so when eur2usd
is called, +eur2usd-factor+
is actually 0.9267.
However, after the let
, everything is as it has been before:
(eur2usd 10) ; => 13.068
Now, how can we translate this behavior into Ruby. I decided to go the way of using a thread-local variable (a Hash, actually) and add convenience functions to define and change and rebind them. Let’s see how above code looks in Ruby:
require 'dynamic'
Dynamic.variable :eur2usd_factor => 1.3068
def eur2usd(euro)
euro * Dynamic.eur2usd_factor
end
p eur2usd(10) # => 13.068
p eur2usd(0.77) # => 1.006236
Dynamic.let :eur2usd_factor => 0.9267 do
p eur2usd(10) # => 9.267
p eur2usd(0.77) # => 0.713559
end
p eur2usd(10) # => 13.068
Now, that was pretty easy. You basically just need to prefix your
variable with Dynamic.
to look it up. It’s also a nice sample how
to extend Ruby to add new language features, by definining some
convenient methods alone.
As you should use dynamic variables as globals only, it does no harm that Dynamic is implemented using a singleton. Dynamic is thread-safe however, in that you can’t change other thread’s environment, and get a new copy of the main thread’s environment.
Now, go ahead and grok the code. Happy hacking.
NP: The Distillers—Gypsy Rose Lee