Thinking about Ruby's Symbols

I just returned from teaching Ruby & Rails in L.A. and I got thinking about Ruby’s Symbols. Symbols quickly become second-nature to Rubyists, but they’re difficult to teach.

Those new to Ruby see something like:

Person.where(:name => 'Daniel')

or

<%= form.text_field :name %>

and begin to get confused. Even when I start in plain Ruby and introduce symbols, usually saying “they’re mainly used as keys in a Hash” I get strange looks. They try to figure out what a symbol “means” and think of it as a variable. This is understandable. I usually try to tell those hung up on the “why” of symbols to give them a chance and they’ll soon understand how symbols are used.

In a case like validations or associations, they get confused because we’re using symbols instead of the actual thing: the column, model, or table.

validates :name, :presence => true

belongs_to :company

Seasoned Rubyists know that we do that because we’re not referring to the actual thing. We don’t want the column value, we want an identifier to that thing that we can use later.

Hashes

Then we see things in Ruby that look like symbols, but backwards.

Console/log output:

> Person.where(:name => 'Daniel').first
=> #

Ok, that’s just some output, and it looks more like JSON than Ruby, but we’ll frequently end up setting (and getting) them using a hash, making it more confusing.

person = Person.new(:name => 'Daniel', :style=> 'awesome')
person[:name] = 'Dan'

I think this waters down symbols a bit, but really you don’t have to truly understand symbols to use Ruby or Rails, just know when they’re used.[1]

Ruby 1.9

Symbols might become even more confusing with Ruby 1.9’s new Hash syntax:

Person.create(name: 'Daniel', style: 'awesome')
Person.where(name: 'Daniel')

What?!? So we’re defining hashes with symbol keys, and a symbol is defined as beginning with a colon (:name) but we’re writing it with a colon on the other side?!?

Look at this and tell me it isn’t confusing:

> h = {name: 'Daniel'}
=> {:name=>"Daniel"} 
> h[:name]
=> "Daniel" 
> h[name:]
SyntaxError: (irb):3: syntax error, unexpected ']'

I’m not sure this will make explaining symbols any easier. It sort of meshes with the console output, but even irb displays using the older syntax. I’m actually a fan of the new syntax[2]. It looks more like JavaScript (and Python) and saves characters, but unfortunately I suspect it will create new confusion.

Take this example:

validates :name, presence: true

:name is a pure Symbol, so the colon goes before. presence: is a symbol, but used as a Hash key. Sheesh.

Final Thoughts

We definitely want to move the language forward, but we have to be careful to not confuse new users. A common source of bugs among my training students is missing colons, thereby raising unexpected exceptions.

I’m not sure what’s needed, but maybe a rethink of how we access hashes. Would it make things easier if h\[name:\] worked? Would it be better if all hashes acted like HashWithIndifferentAccess and we could access them with h\['name'\]? I’m not sure.

Symbols are a fantastic Ruby concept and make the language much more expressive. When combined with good syntax highlighting, they also help differentiate code from strings. Unfortunately, they’re only going to get more confusing for newbies.

[1] I’m a believer that mastery comes with time. For example, I didn’t truly understand how .each {} worked while I was getting started with Ruby, but it didn’t hold me back from using it. You need to recognize and be able to use the syntax, not know how passing blocks works behind the scenes.

[2] I wish it would have been done this way originally. The community isn’t bold enough to rip out the old way completely in 2.0. Maybe 3.0?

Photo of Daniel Morrison

Daniel founded Collective Idea in 2005 to put a name to his growing and already full-time freelance work. He works hard writing code, teaching, and mentoring.

Comments

  1. October 24, 2011 at 11:20 AM

    Can you mix and match the new hash syntax with the old?

  2. October 24, 2011 at 11:21 AM

    I thought not, but I tried it in irb and sure enough:

     > {foo: 1, :bar => 4}
     => {:foo=>1, :bar=>4}
    

    Crazy!

  3. October 24, 2011 at 11:22 AM

    What really grinds my gears about the new syntax is that it doesn’t support strings for keys which would bring it more in line with the Javascript syntax. Wasn’t that part of the whole reason for the new syntax to begin with?

    > { "foo": 2 }
    # => Syntax error
    
  4. October 24, 2011 at 12:03 PM

    I really dislike the new syntax for many of the reasons stated here. For a while though, it seemed everyone was sticking with the old style anyway. Recently I’ve seen 1.9 syntax creep into projects though. The incosistency really bothers me.