1. Computing

Constant Variables

By

The speed of light (unless you've read the recent news) is the constant and unbreakable speed limit of the universe. If we were to represent the speed of light in a Ruby program, using a variable might be a bit misleading. If it never changes, why store it in a variable at all? Enter the constant variable.

Constant variables are variables that once assigned to cannot be assigned to again. (This is not strictly true, but it is how they are used.)  Think of them like "read only" variables. They occupy entries in a binding, just like local variables (or any other variable, really), and have similar scope rules to local variables. Constant variables are denoted by their first character being uppercase and, by convention, all other characters being uppercase and SNAKE_CASE being used instead of CamelCase.

So, for example, if we're doing some physics calculations involving the gravitational force between two bodies, we're going to need to define the gravitational constant. This is a true constant, in none of your calculations will it ever change. It's usually referred to as simply G in calculations, but in Ruby we like to be a bit more verbose. We're going to call it GRAVITATIONAL_CONSTANT. Note that the constant is scoped like a local variable here, but constants can have other scopes which will be discussed below.


def gravitational_force(m1, m2, dist)
  GRAVITATIONAL_CONSTANT = 6.67259e-11
  return ( m1 * m2 * GRAVITATIONAL_CONSTANT ) / dist**2
end

It might even seem silly to even define the gravitational constant in this method. After all, it was just used once in the line immediately following. It's always a good idea to avoid magic numbers in any calculations you do. Ideally, all numbers should have a variable or constant name to at least describe what it is. What if someone unfamiliar with physics is reading your code? What if this code is modified and moved around combined with other equations? This is one of the biggest uses for constants, to avoid magic numbers.

Class Constants

Constants aren't usually sitting around as local variables unless their usage is really that limited. Typically, they're in the class scope, meaning all class and instance methods have access to them, as well as anything outside the class using the scope resolution operator. Classes or modules often have a list of constant variables near the top of their definition that are used within, or are sometimes simply a container for constant variables.

So, continuing with the physics calculations, we'll want a module full of physics constants we can use if we ever might need them. This module doesn't even do anything, it has no methods. We only need it to hold the numbers and be referred to using the module name, the scope resolution operator (::, two colon characters) and the constant name. For example, from any point in the program we can now refer to the gravitational constant as Physics::GRAVITATIONAL_CONSTANT and the speed of light as Physics::SPEED_OF_LIGHT.


module Physics
  SPEED_OF_LIGHT = 2.99792458e8
  CHARGE_OF_ELECTRON = 1.602177e-19
  GRAVITATIONAL_CONSTANT = 6.67269e-11
  PLANCKS_CONSTANT = 6.6260755e-34
  MASS_OF_ELECTRON = 9.10939e-31
  MASS_OF_NEUTRON = 1.67262e-27
  MASS_OF_PROTON = 1.67492e-27
end

# At any other point in the program, you can
# refer to these constants.
puts Physics::MASS_OF_ELECTRON

if Physics::MASS_OF_PROTON > Physics::MASS_OF_NEUTRON
  puts "Protons are slightly more massive"
end

Constants of Another Type

Constants are just like any other variable in the fact that they can refer to any object they wish. Constants need not be numeric, they can refer to any objects including objects containing other objects. However, this is where the first gotcha appears with Ruby constants.

Examine the following code. It assigns an array to a constant variable. That array can then be accessed as any other array.


FOUNDING_FATHERS = %w{ Jefferson Washington Franklin }
FOUDNING_FATHERS.each {|f| puts f }

Not Quite Constant

In some languages like C, constants are implemented as preprocessor symbols, meaning they don't even survive the parsing stage. After parsing, they're just numbers inside of the code. They aren't variables at all. Other languages, like C++, implement very strict constants. They are variables, and can be referred to as any other variables, but C++ goes through some trouble to prevent them from ever being modified. Ruby is not quite so strict about its constants.

In Ruby, once you assign to a constant, you cannot reassign to that constant without a warning. You read that right, it's only a warning to assign to an already assigned constant variable. Further, Ruby only gives this warning if you try to change the object that the constant variable refers to. It says nothing about changing the object itself. The following code should not produce any warnings at all.


ARRAY = [1,2,3]
ARRAY << 4

Since the array append operator doesn't try to make the constant variable point to another object, Ruby is just fine with this statement. If you were to try with the += operator, which creates a new array object an assigns it to the variable, it will work but you'll get a warning.


$ ruby
ARRAY = [1,2,3]
ARRAY += [4]
-:2: warning: already initialized constant ARRAY

For this reason, to prevent accident (and silent!) modification of constants, constant value are often frozen. Once an object is frozen, it can't accidentally be modified without unfreezing it. So, if you were to accidentally map! a constant (with the exclamation point, which modifies the object) instead of map, Ruby would raise an exception since the constant Array was frozen.


#!/usr/bin/env ruby
ARRAY = [1,2.3].freeze

# Accidental destructive map!
ARRAY.map!{|i| i+1 }

So when you're working with Ruby, just know that constants are largely a convention. There's no guarantee that they'll really be constant, though in normal operation they should be constant.

©2014 About.com. All rights reserved.