Ruby Hash syntax

Dear Ruby, I love you. But I just can’t get used to that new-fangled Hash syntax of yours:

h = {foo: :bar}

It smells of Javascript-envy, and it doesn’t suit you.

I know I am too late, I know I’ve had my chances at saying no, but I have to get this rant off my chest.

It’s confusing

When I come across the following, I get confused:

h = {foo: :bar}

What the hell is that foo-thing? A method call foo followed by a colon? Or perhaps some sort of reversed symbol, foo:?

Surely, it must be something entirely different from :bar, which clearly looks entirely different.

There is no obvious way to reason about it, except knowing this special case rule that it’s really a symbol, that looks like a method call.

And I’m not the only one confused by this.

It’s inflexible

Alright, so since we know it’s a symbol, we’d like to make that more obvious, so we change it to

h = {:foo: :bar}

But nooooo, that’s not how it works. Neither can we do

h = {"foo": :bar}

It really is a very special case; nothing but Symbols can use : as a key/value separator. Lucky symbols, screw you other less worthy objects.

“Only some symbol keys can be used in this fashion; for example, {:$set => ‘b’} is valid whereas {$set: ‘b’} is not. AFAIK, only symbols that are also valid labels can be used with the JavaScript-ish syntax.” – mu is too short

Except in Ruby 2.2 (Updated 2015-02-02)

With the recent release of Ruby 2.2, the above is no longer accurate. With Ruby 2.2 you can indeed do

h = {"foo": :bar}

Ruby still really wants you to use a Symbol, though, so the key will stealthily (no warnings, nothing) be changed from a String to a Symbol behind your back:

h = {"foo": :bar} #=> {:foo=>:bar}
h["foo"] #=> nil

You still have to use hash rockets

So even if you don’t like the rockets, you still have to use them for the cases where you need String-based keys. This means you will likely have both JSON-style and Ruby-style hashes smattered between each other with no rhyme or reason.

And hey, if you’re really lucky you might even end up with something like this eye-sore:

  symbol: "It's a Symbol",
  "String" => "It's a String"

It looks like keyword arguments, but isn’t

Even though they appear exactly the same, the foo: "bar" in

h = {foo: "bar"}

is vastly different from the foo: "bar" in

def method(foo: "bar")

For example, as of Ruby 2.1 you can do

def method(foo:)

You can’t do

h = {foo:}

It’s special cases all the way down!

Ruby doesn’t really believe in it

Ruby doesn’t even believe in the new syntax and returns hash-rocket style output:

h = { foo: 'bar' }
#=> {:foo=>"bar"}

It even expects hash-rockets if you accidentially trigger a syntax error (I accidentally left a trailing comma here):

method1 bar: baz,

SyntaxError ((irb):8: syntax error, unexpected '\n', expecting =>)

Get off mah lawn!

I get it. The new syntax lets old Ruby versions have something resembling named keyword arguments using a Hash instead - something Avdi showed in RubyTapas episode 186 on Keyword Argument.

That’s a fine semantic meaning for that syntax. Use it for that, but don’t use it anywhere else, where all it adds is confusion and inconsistencies.