I've been working on trying to better understand Ruby and here's something I'm having trouble with:
$SAFE = 1
puts $SAFE # 1
proc {
$SAFE = 2
puts $SAFE # 2
}.call
puts $SAFE # 1
The above code is partially taken from eRB's source rewritten to better highlight the example. Basically within the proc one can set the value of $SAFE
to whatever value one wants and after the proc, the value of SAFE
returns back to what it was before the proc.
If instead of using the word $SAFE
I change it to a different word, such as $DOOR
:
$DOOR = 1
puts $DOOR
proc {
$DOOR = 2
puts $DOOR
}.call
puts $DOOR
then the value of $DOOR
after the proc is 2 and not 1. Why the difference between the two examples?
The first declaration of a global variable establishes the variable in the global environment; subsequent global declarations simply bind the variable to the global environment and establish the value of the variable at that point. CREATE PROCEDURE proc2() ... DEFINE GLOBAL gl_out INT DEFAULT 23; DEFINE tmp INT; ...
Global variables can be used by everyone, both inside of functions and outside.
The global command will cause a variable in a local scope (inside a procedure) to refer to the global variable of that name. The upvar command is similar. It "ties" the name of a variable in the current scope to a variable in a different scope. This is commonly used to simulate pass-by-reference to procs.
It's rather simple, really: the reason why $SAFE
doesn't behave like you would expect from a global variable is because it isn't a global variable. It's a magic unicorn thingamajiggy.
There are quite a few of those magic unicorn thingamajiggies in Ruby, and they are unfortunately not very well documented (not at all documented, in fact), as the developers of the alternative Ruby implementations found out the hard way. These thingamajiggies all behave differently and (seemingly) inconsistently, and pretty much the only two things they have in common is that they look like global variables but don't behave like them.
Some have local scope. Some have thread-local scope. Some magically change without anyone ever assigning to them. Some have magic meaning for the interpreter and change how the language behaves. Some have other weird semantics attached to them.
$SAFE
has almost all of the above: it is thread-local, meaning that if you change it in one thread, it doesn't affect other threads. It is local, meaning if you change it in a local scope (like a class, module, method or block), it doesn't affect the outer scope (as you have discovered). It has magic meaning for the interpreter, since setting it to a value different than 0
makes certain things not work. And it has additional weird semantics in that you can only ever increase its value, never decrease it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With