by

Hash syntax in Ruby 2.2

Twice in the last three weeks, I have stumped into a relatively recent addition to Ruby’s syntax: a new way to define symbol keys in a Hash literal.

Since Ruby 2.2, the following syntax is legal:

1{ "foo": "bar" }

The weird thing here is that the above literal doesn’t mean what I thought it meant. This is what I expected it to be equivalent to:

1{ "foo" => "bar" }

And this is what it actually is equivalent to:

1{ :foo => "bar" }

I have to admit that both meanings are perfectly reasonable in their own way. To me, it was annoying because I copied over some JSON into my code and forgot to change the colons : to hash rockets =>. My tests started failing and it took me a bit to realise what was going on. An older Ruby would have given me a syntax error and I would have realised straightaway.

Another reason I find it surprising is because, since the introduction of the colon-based syntax for hashes in Ruby 1.9 (eg: {foo: "bar"}), I have had some opportunities to see mixes of colons and hash bangs such as this one:

1{
2  foo: "bar",
3  1 => "baz",
4  "uno" => "dos",
5}

I was hoping that, eventually, Ruby would get a syntax compatible with JSON’s, so that I could just copy the latter into the former without the problem described above, just as you can in JavaScript. The syntaxes of both were not compatible but didn’t have any conflicts, and Ruby just needed extending the colon syntax to values other than symbols. However, the change in 2.2 eliminates this possibility, as it introduces an element that means different things in each format.

I have no idea of what the exact line of thought was in Ruby’s core team when they decided to introduce this (I haven’t been able to find a relevant link). However, I did find a defence of it on StackOverflow.

The argument goes that symbols need to be defined with quotes when they have certain characters—eg: :"strange-symbol". Therefore it makes sense that these two keys both are interpreted as symbols:

1{
2  normal_symbol: "foo",
3  "weird-symbol": "bar",
4}

Which ok, I admit also makes sense, but it sure is weird to me.