Why Are Symbols in Ruby Not Thought of as a Type of Variable

Why are symbols in Ruby not thought of as a type of variable?

Symbols used in accessor methods are not variables. They are just representing the name of a variable. Variables hold some reference, so you cannot use a variable itself in defining accessor methods. For example, suppose you wanted to define an accessor method for the variable @foo in a context where its value is "bar". What would happen if Ruby's syntax were to be like this:

attr_accessor @foo

This would be no different from writing:

attr_accessor "bar"

where you have no access to the name @foo that you are interested in. Therefore, such constructions have to be designed to refer to variable names at a meta level. Symbol is used for this reason. They are not variables themselves. They represent the name of a variable.

And the variable relevant to accessor methods are instance variables.

What is the difference between a symbol and a variable in Ruby?

A symbol is an "internalized" string, it's more like a constant than anything. Typical example:

account_details = {
:name => 'Bob',
:age => 20
}

Here the symbols :name and :age are keys for a hash. They are not to be confused with variables. account_details is a variable.

A variable in Ruby is a handle to an object of some sort, and that object may be a symbol.

Normally you employ symbols when using strings would result in a lot of repetition. Keep in mind that strings are generally distinct objects where a distinct symbol always refers to the same object, making them more efficient if used frequently.

Compare:

"string".object_id == "string".object_id
# => false

:string.object_id == :string.object_id
# => true

Even though those two strings are identical, they're independent string objects. When used as keys for hashes, arguments to methods, and other common cases, these objects will quickly clutter up your memory with massive amounts of duplication unless you go out of your way to use the same string instance. Symbols do this for you automatically.

What is the use of symbols?

Ok, so the misunderstanding probably stems from this:

A symbol is not a variable, it is a value. like 9 is a value that is a number.

A symbol is a value that is kinda of roughly a string... it's just not a string that you can change... and because you can't change it, we can use a shortcut -> all symbols with the same name/value are stored in the same memory-spot (to save space).

You store the symbol into a variable, or use the value somewhere - eg as the key of a hash.... this last is probably one of the most common uses of a symbol.

you make a hash that contains key-value pairs eg:

thing_attrs = {:name => "My thing", :colour => "blue", :size => 6}
thing_attrs[:colour] # 'blue'

In this has - the symbols are the keys you can use any object as a key, but symbols are good to use as they use english words, and are thus easy to understand what you're storing/fetching... much better than, say numbers. Imagine you had:

thing_attrs = {0 => "My thing", 1 => "blue", 2 => 6}
thing_attrs[1] # => "blue"

It would be annoying and hard to remember that attribute 1 is the colour... it's much nicer to give names that you can read when you're reading the code. Thus we have two options: a string, or a symbol.

There would be very little difference between the two. A string is definitely usable eg:

thing_attrs = {"name" => "My thing", "colour" => "blue", "size" => 6}
thing_attrs["colour"] # 'blue'

except that as we know... symbols use less memory. Not a lot less, but enough less that in a large program, over time, you will notice it.
So it has become a ruby-standard to use symbols instead.

How to represent a value with symbol in ruby?

I think you are confused about the use of symbols by some of the conventions in Ruby and Rails.

Symbols are not variables. Variables are used to store values. Symbols are lighter weight versions of strings. They can be used in place of strings in places like hash keys.

hash1 = {'name' => 'Mary', 'age' => 30}
puts hash1['name']
#=> 'Mary'

hash2 = {:name => 'John', :age => 32}
puts hash2[:age]
#=> '32'

Ruby introduced a new hash notation to make things cleaner when using symbols for hash notation that eliminated the "hash rocket" =>

hash2 = {name: 'John', age: 32}

To take advantage of the conventions in Rails they came up with "hash with indifferent access". So it is a hash that allows you to use either a string or its symbol version interchangeably in a hash:

hash2 = = ActiveSupport::HashWithIndifferentAccess.new
hash2['name'] = 'John'
puts hash2[:name]
#=> 'John'
puts hash2['name']
#=> 'John'

In Rails you assign values to columns in a table using symbols:

p = Person.new(name: 'John', age: '32')

you then access them through method names that are string versions of the column name:

puts p.name
#=> 'John'

I think you are missing some real foundations of Ruby. I would study that more and then maybe do one of the tutorials that involves rebuilding Rails so you see how the syntax of Ruby relates to the conventions in Rails.

How symbols are resolved in ruby

The count was the same; i.e., adding def a did not increase the symbol table count.

This is because symbol :a already exists:

$ ruby -e "puts Symbol.all_symbols.count"
2504

$ ruby -e "puts Symbol.all_symbols" | grep ^a | sort | head -n 5
a
abort
abort_on_exception
abort_on_exception=
abs

You can start Ruby with the --disable-gems options to get rid of it (and many other symbols):

$ ruby --disable-gems -e "puts Symbol.all_symbols.count"
1689

$ ruby --disable-gems -e "puts Symbol.all_symbols" | grep ^a | sort | head -n 5
abort
abort_on_exception
abort_on_exception=
abs
abs2

Now, defining or referencing a actually increases the symbol count:

$ ruby --disable-gems -e "puts Symbol.all_symbols.count"
1689

$ ruby --disable-gems -e "def a; end; puts Symbol.all_symbols.count"
1690

$ ruby --disable-gems -e "a = 1; puts Symbol.all_symbols.count"
1690

When is it correct to use symbols in place of variables in Ruby?

Symbol is a value. In your example you need a variable to store the Error object. You usually use symbols as string constants.

For example, if you create a module with cardinal directions it is better to use the symbols :north, :south, :east, :west rather than the strings "north", "south", "east" and "west".

Symbols are often used as keys in hashes:

my_hash = { a: 1, b: 7, e: 115 }

It's very useful to read ruby code on github for instance in order to understand when to use symbols.

How to understand symbols in Ruby

Consider this:

x = :sym
y = :sym
(x.__id__ == y.__id__ ) && ( :sym.__id__ == x.__id__) # => true

x = "string"
y = "string"
(x.__id__ == y.__id__ ) || ( "string".__id__ == x.__id__) # => false

So, however you create a symbol object, as long as its contents are the same, it will refer to the same object in memory. This is not a problem because a symbol is an immutable object. Strings are mutable.


(In response to the comment below)

In the original article, the value is not being stored in a symbol, it is being stored in a hash. Consider this:

hash1 = { "string" => "value"}
hash2 = { "string" => "value"}

This creates six objects in the memory -- four string objects and two hash objects.

hash1 = { :symbol => "value"}
hash2 = { :symbol => "value"}

This only creates five objects in memory -- one symbol, two strings and two hash objects.

Understanding Ruby variables and symbols?

Variables starting with @ are instance variables, "properties" in other languages. Whereas 'classic' variables are local to the scope of their method/block, instance variables are local to a specific instance of an object, for example:

class Foo

def initialize(bar)
@bar = bar
end

def bar
@bar # the variable is specific to this instance
end

def buzz
buzz = 'buzz' # this variable is not accessible outside of this method
end

end

You may also see variables starting with @@, which are class variables, and are accessible by every instance of the class and shared with every instance of the subclass. Usage of those variables is usually discouraged, primarily because subclasses share the variable, which can cause a lot of mess.

In Ruby everything is an object, classes are objects (instances of class Class), so you can also have class instance variables:

class Foo

def self.bar
@bar #we are in class Foo's scope, which is an instance of class Class
end

def self.bar=(bar)
@bar = bar
end

def bar
@bar # Foo.new.bar != Foo.bar
end

end

What you call "variables with a colon" are not variables. They are a particular type of string, called a symbol, that is immutable and optimized for quick identification by the interpreter, in fact, those are stored internally as pointers, so that :this == :this is a very quick operation.

This property makes them good candidates for hash keys because they offer quick retrieval or for "flags" to pass to a method; Think of them as a sort of loose constant that "stands for" what they say. Their immutability is also dangerous: All symbols ever created never get garbage collected; It's easy to create a memory-leak by creating thousands of symbols, so use them wisely.

UPDATE since ruby 2.2 symbols may be garbage-collected in certain cases (when no reference is kept and no comparison is needed)



Related Topics



Leave a reply



Submit