Symbols
Variables are always a pairing of two things: a name and value. Could you imagine a situation where you wanted just a name without an associated value? Consider this snippet of Ruby that updates an xy-position according to the direction an entity is facing:
if direction == 1
y += 1
elsif direction == 2
x += 1
elsif direction == 3
y -= 1
else
x -= 1
end
The problem with this code is that the direction numbers have no inherent meaning. Developers must remember that 1 means north, 2 means east, 3 means south, and 4 means west. Meaning can be restored by using strings as flags instead of numbers:
if direction == 'north'
y += 1
elsif direction == 'east'
x += 1
elsif direction == 'south'
y -= 1
else
x -= 1
end
This reduces the cognitive burden, but it has an impact on performance. Strings are more expensive to compare than integers.
A few languages provide a feature for introducing meaningful names that have no meaningful value and that can be compared quickly—in constant time. These are symbols. In Ruby, a symbol literal is an identifier preceded by a colon. This code is both readable and fast:
if direction == :north
y += 1
elsif direction == :east
x += 1
elsif direction == :south
y -= 1
else
x -= 1
end
The first time the Ruby interpreter encounters a particular symbol, it assigns the symbol a unique integer and stores or interns the association in a global dictionary. Any later references to that symbol in any scope will look up the symbol in the dictionary and evaluate to that same integer.
The particular integer assigned to a symbol is unimportant beyond its uniqueness. But Ruby does let you inspect the integer with the object_id
method.
Symbols are supported in several other languages, including Lisp and JavaScript. A similar effect can be achieved in C, C++, and Java using enums.