0

I've read C2Wiki on Global Variables, and I have three questions about them (at the bottom of this post). The main question is: If Global Variables are so bad, why does Ruby have them?

Also, I've noticed some interesting behaviour regarding Global Variables in Ruby, that causes them to work differently to regular global-level constants.

1. Referencing an undefined Global Variable returns nil. Referencing an undefined global constant returns a NameError:

2.2.3 :001 > $THING
 => nil 
2.2.3 :002 > THING
NameError: uninitialized constant THING
    from (irb):2
    from /Users/makerslaptop82/.rvm/rubies/ruby-2.2.3/bin/irb:15:in `<main>'

2. irb is initialized with both $stdout and STDOUT defined. You can redefine $stdout, and this doesn't affect STDOUT:

2.2.3 :001 > $stdout
 => #<IO:<STDOUT>> 
2.2.3 :002 > STDOUT
 => #<IO:<STDOUT>> 
2.2.3 :003 > $stdout = IO.new(6)
 => #<IO:fd 6> 
2.2.3 :004 > $stdout
 => #<IO:fd 6> 
2.2.3 :005 > STDOUT
 => #<IO:<STDOUT>> 

My questions are:

  • If Global Variables are so bad, why does Ruby have them?
  • Why does referencing undefined Global Variables return nil instead of a NameError? Was this choice deliberate? Why?
  • Is there some danger to having two virtually-identically-named versions of STDOUT in a single program? (I assume there are other globally-defined objects that this applies to as well)
Sam Morgan
  • 228
  • 1
  • 11
  • FWIW, some options I considered: 1. Ruby is super-permissive and so it wants you to be able to do stuff (even if you shouldn't). Doesn't answer q2 or 3. 2. Some genuine representation of 'globality', rather than something simply defined at global scope, might have meaning for some programmes. But that doesn't really answer the other two questions either. 3. Globals are included solely for the purpose of programme IO to have multiple options: a constant one and some more flexible output. But that doesn't really answer question 2, as a `nil` is more dangerous than raising a `NameError`. – Sam Morgan May 26 '17 at 13:50
  • 2
    You are asking why Matz made certain design decisions when creating Ruby. We could speculate about that but would not be able to provide definitive answers. Presumably, Matz believes there is a place for global variables in Ruby as he defined several (such as `$~`). Moreover, many programming languages support global variables so it's hard to make the case that they should be banned. Imo, this question calls for opinion and therefore is not suitable for SO. – Cary Swoveland May 26 '17 at 14:25
  • 6
    You are basically asking "What was matz thinking when he designed Ruby". Unfortunately, [so] cannot answer this question, only matz can. You'll need to ask him. – Jörg W Mittag May 26 '17 at 14:30

1 Answers1

5

Global variables are not bad. They're not evil. They're just incredibly, incredibly powerful. Which is why you shouldn't use them.

Global variables are global- they can be accessed and modified anywhere in the code. A single global variable has the potential to affect all of your classes, all of your functions, all of the classes and functions of every single library or dependency you load into your project, and all of the classes and functions of every single project which loads your project as a dependency, as well as the projects that load those projects, and so and and so forth, for ever and always, for the rest of time.

The second people start feeling comfortable using global variables, the namespace gets insanely cluttered and we get conflicts left and right and the stability of the programming language itself is threatened. Which is why the use of global variables is emphatically and repeatedly discouraged.

But global variables are not bad. They're like the highway lanes labeled "for emergency vehicles only," or like those fire-axes behind glass labeled "break glass in case of emergency."

It's entirely possible that at some point, in the distant future, you will have an incredibly unusual situation which merits the use of a single global variable. But that day is not today. And it is probably not tomorrow, or a month from now, or a year from now. Daily life, daily code- it just doesn't call for the unbridled power of a global variable.


$stdout is a great example of why global variables are sometimes important. $stdout is the default stream in ruby- the one where things will print if no other stream is specified. $stdout should be accessible from every class and every function in every library because it acts like a giant funnel, shoveling all output to a single location. The whole world knows and agrees that $stdout exists in ruby, and its uses are well-documented, so its power is well-managed.

This isn't to be confused with STDOUT which is a constant representing the actual pipe which sets up a stream between ruby and its parent program (usually a terminal). $stdout = STDOUT by default, but $stdout can be changed to anything. If you want your program to print to a file, you can change $stdout to a file stream.

I don't think this name choice is confusing for a seasoned rubyist. A variable is designed to be modified and a constant is designed to be constant. The difference between $stdout and STDOUT is that the former can be modified to change the standard output location of your program, and the latter is a constant, always pointing to the stdout stream. The capitalization makes a world of difference and conveys very different meanings.


As for why global constants are uninitialized and global variables are nil, that actually has nothing to do with globals. Ruby automatically initializes all variables as nil. You can easily see this with instance variables such as @foo or @@foo. In almost every situation, an undefined local variable will throw a NameError because ruby cannot tell whether it is a variable or a method. But in strange situations, they too are initialized as nil:

puts foo # => NameError: undefined local variable or method 'foo'
foo = 42 if false
puts foo # => nil

puts bar # => NameError
bar = bar
puts bar # => nil

It was a conscious design choice in Ruby not to automatically initialize constants. Because a constant is, by definition, something which is initialized once and then never changed, it would break the definition for a constant to be nil at first and then a different value later in the code.


I should also mention that global constants are considered acceptable, even among people who tout global variables as bad. The difference is that constants can only be assigned once and generally throw a warning or error if they get assigned again. This protects programmers from situations where conflicting global constants might cause problems.

eiko
  • 5,110
  • 6
  • 17
  • 35
  • 1
    Good answer. One area where I've seen global variables used to advantage is in debugging code. One example is puts-ing to a logging file (named `$log_file_name`). I suppose in some cases that would be helpful even in production code. If the value of the global variable is not to be changed (as in my example). one could of course define a constant in a module that is included everywhere logging is done. – Cary Swoveland May 26 '17 at 17:48
  • Yes on uninitialized instance (or global) variables returning `nil`; no on class (or local) variables doing so. – Cary Swoveland May 26 '17 at 18:09
  • @CarySwoveland if the global is not to be changed, then there's no reason a constant couldn't be used, except for maybe to imply "this is not normal code" – max pleaner May 26 '17 at 18:09
  • @max, maybe I wasn't clear. I meant "one could of course [instead] define a constant...". – Cary Swoveland May 26 '17 at 18:11
  • @CarySwoveland thanks for catching that. i misspoke when i said the distinction was "heap-allocated" variables. i've corrected my answer. – eiko May 26 '17 at 18:22