Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
R.I.P. Ruby Hash Rocket Syntax 1993-2010 (peepcode.com)
159 points by there on Jan 5, 2011 | hide | past | favorite | 92 comments


In related news, Hashrocket (the consultancy) is alive and well, with no intentions of changing our name to Hashcolon. http://hashrocket.com/

In fact, like almost everyone else in our space we're looking to hire => http://hashrocket.com/jobs


Rebranding to "Colon" just isn't as sexy.


    :sex
Sex symbol, or colon sex?



It's not reddit, but I upvoted you anyway.


I imagine what you're looking for most is people for reliability and/or scaling?

The jobs link is down.


I was actually surprised that the Hashrocket/Ruby 1.9 syntax joke didn't come up.


Maybe you should change it to Fatcomma? That's what it's called in Perl, where the syntax is still used.


I have to say I cried a little bit when I read this - it seems that Matz et al are trying to make the language unparseable.

(Of course, perhaps I'm more sensitive than most, having actually written an Ruby lexer - http://github.com/jasonl/eden - which made me deal with the dusty, hidden corners of the Ruby grammar.)


How does this make the language harder to parse?


Only ruby will be able to parse Ruby ;-)

Ruby is just showing its Perl roots.


Given that the hashrocket came from Perl to Ruby primarily, and that it changed to look more like Python, I'd say you're wrong :)


Well, the pun was not about the => operator, but about Ruby becoming "less parseable". As they say: "Only perl can parse Perl" ;-)

So => is the "hashrocket" operator? I can't find any reference to this name besides this: http://www.ruby-forum.com/topic/152544


I suppose. It goes well with the spaceship operator <=>.


Now we need to find a meaning for |=| so it can be the Tie Fighter operator.


also says this syntax is compatible with python which is not really true. if you have this in python:

    {
      key: "value",
      dr_nic: "The Comedian",
      ttl: 42
    }
python will expect there to be a variable called 'key', one called 'dr_nic', and one called 'ttl'. If you intend to do

    d['ttl']
you'll need to create a hash using a literal string


Yea. So they've move away from the Perl-like 'rocket' syntax to a more JavaScript/Python-like syntax. Ruby+Python hackers will rejoice while Perl+Ruby hackers will pout. This isn't necessarily some 'huge' win.

[Though I guess it could be a 'win' for Rails hackers since they are likely to just be Ruby/Rails+JavaScript hackers.]


As a Perl developer I'd say »More power to them.« Also, since Perl's => is basically a comma operator that turns a bareword on its left into a string, I'd say Ruby's move is a step away from Perl regarding syntax, but a step towards Perl regarding functionality.

Anyway, as a non-Ruby developer I can appreciate the move. Also reminds a bit of some Scheme's ability to prefix and postfix symbols with »:«.


You don't necessarily have to create a hash using a literal string. You can also do this:

    dict(
        key="value",
        dr_nic="The Comedian",
        ttl=42
        )
...but that is a bit different syntax.


Yeah.

I always do in such way. And I will continue even if it's a bit slower =)


It will also make your code drastically slower.


Slower yes, but not drastically:

    % python -m timeit '{"key": "value", "dr_nic": "The Comedian", "ttl": 42}'
    1000000 loops, best of 3: 0.296 usec per loop

    % python -m timeit 'dict(key="value", dr_nic="The Comedian", ttl=42)'
    1000000 loops, best of 3: 0.771 usec per loop
There are far better ways to make code faster than worrying about the extra fraction of a microsecond you're spending calling dict rather than using the literal syntax.


Slower than what alternative?


Slower than my_dict = { "foo": 1, "baz": 2 }

I was unaware that there was a difference in performance, but a quick timing check shows that it takes about twice as long to create a dictionary with 7 items using dict() than using {}.

It makes sense when I stop to think about what the code would have to do, though.


You're right; I'm seeing dict() with explicit kwargs taking 3 times as long as a literal. My unscientific and non-rigorous benchmarks tell me that the construction and unpacking of the kwargs in the dict() call are the cause of most of the slowdown.

With the time it takes to assign with a literal normalized to 1, I'm seeing dict(kwargs) (with kwargs created before the benchmark) take 2 and dict(key_1=val_1, key_2=val_2, etc.) take 3.


Unpacking kwargs is pretty much equivalent to building a dict. The major slowdown is 1) looking up dict and 2) calling a function:

    >>> def dict1():
    ...     {"a" : 1, "b" : 2}
    ... 
    >>> from dis import dis
    >>> dis(dict1)
      2           0 BUILD_MAP                2
                  3 LOAD_CONST               1 (1)
                  6 LOAD_CONST               2 ('a')
                  9 STORE_MAP           
                 10 LOAD_CONST               3 (2)
                 13 LOAD_CONST               4 ('b')
                 16 STORE_MAP           
                 17 POP_TOP             
                 18 LOAD_CONST               0 (None)
                 21 RETURN_VALUE        
    >>> def dict2():
    ...     dict(a=1, b=2)
    ... 
    >>> dis(dict2)
      2           0 LOAD_GLOBAL              0 (dict)
                  3 LOAD_CONST               1 ('a')
                  6 LOAD_CONST               2 (1)
                  9 LOAD_CONST               3 ('b')
                 12 LOAD_CONST               4 (2)
                 15 CALL_FUNCTION          512
                 18 POP_TOP             
                 19 LOAD_CONST               0 (None)
                 22 RETURN_VALUE        
Given that, I'd assume that the overhead doesn't grow as the dict grows.


As python runtimes/JITs get better and better, the literal version could get relatively faster, since static assertions can potentially more easily be made about it; whereas the global 'dict' could be replaced with anything, requiring much more sophistication.

Say you use a dict in a non-mutating manner, it is made up of string literals only, and you construct it within a loop; it could be able to be pulled out as an invariant, the same could be true of the function-call case, but potentially not as easily.


In optimizing a dynamic language, the problem of a symbol being replaced like this is no big deal; it's all but the expected norm, and inline caching ought to take care of it.

On invariant code motion: it tends not to be a big win because programmers usually move obvious big code out of loops explicitly. They do that because relying on a compiler optimization to do it is unpredictable; you may accidentally trigger a wall in your optimizer's ability to analyze your code as it grows more complex over time.

But we really are arguing over small potatoes here.


I am not a Ruby programmer. What's the syntax for using a variable as the key, which is actually synonymous with the Python code?


It would be something like this:

    >> a = :foobar
    => :foobar
    >> {a => 1}
    => {:foobar=>1}


Am I reading this right that it only applies if your hash key is a symbol? So really, the hash rocket isn't dead?


Correct. It is wonderful sugar, but somewhat limiting. Realistically, you need to know both syntaxes.

  irb(main):001:0> hash={:"symbol with space" => nil}
  => {:"symbol with space"=>nil}
  irb(main):002:0> hash={"symbol with space": nil}
  SyntaxError: (irb):2: syntax error, unexpected ':',  expecting tASSOC
  hash={"symbol with space": nil}
                          ^
	from /usr/bin/irb1.9.1:12:in `<main>'
  irb(main):003:0> hash={"symbolwithoutspace": nil}
  SyntaxError: (irb):3: syntax error, unexpected ':',  expecting tASSOC
  hash={"symbolwithoutspace": nil}
                           ^
	from /usr/bin/irb1.9.1:12:in `<main>'
  irb(main):004:0> hash={symbolwithoutspace: nil}
  => {:symbolwithoutspace=>nil}
  irb(main):005:0> hash={symbol with space: nil}
  SyntaxError: (irb):5: syntax error, unexpected tIDENTIFIER, expecting keyword_do or '{' or '('
  hash={symbol with space: nil}


My own opinion is that although Ruby allows string-like symbols (like :"symbol with space"), I think they should be avoided. There may be a case for them, but I've not encountered one yet.

The new syntax works only with conventional symbols (no quotes), not with any other key type, so no integers, strings, objects etc. For everything else, you can fall back to the original syntax. You can even mix then if you really feel the need (yech!)

That said, for almost all use cases hashes use symbols as keys, and for these cases the new syntax is much cleaner and I'm glad to see it


Combining Haml with jQuery Mobile makes it pretty much mandatory:

    #main_page{:"data-role" => 'page'}


Haml keys can be plain strings.

    #main_page{"data-role" => 'page'}
`data-` attributes are also special-cased:

    #main_page{data: {role: 'page'}}


I did not know that. Thanks for the heads up, and thanks for Haml and Sass!


You can also use "HTML-style attributes" [1]

    #main_page(data-role='page')
[1] http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#ht...


> That said, for almost all use cases hashes use symbols as keys

I'd say there are more vanilla symbol hash uses by number, but the most interesting and powerful things you can do with hashes don't involve symbols for keys, imho.


Ruby 1.9 still uses hash rocket. I'm using RoR3 on 1.9.2 and I haven't changed any of my hashes.

I guess if you want to save some key strokes and you use symbols as keys you can use the new syntax.

The other cool/different thing about Hash in 1.9 is that it preserves order.


[deleted]


Actually, insertion order preservation is a 1.9 feature:

> Hashes enumerate their values in the order that the corresponding keys were inserted.

from http://www.ruby-doc.org/core/classes/Hash.html


The docs were wrong for a long time, but looking back at the issue, there was a quiet turnaround in http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/....

So, hashes can be considered ordered in 1.9. It's a mistake to corrupt the data structure, in my opinion, but apparently that boat has now sailed. Thanks for the prod to recheck and sorry about the outdated info.


Ouch. Thanks for pointing that one out.

PS. Ruby 1.9 does have a native OrderedHash, the same as the one from ActiveSupport.


That's right. It is nice for DSLs though!

http://beastaugh.github.com/stylish/ruby19.html


In case anyone doesn’t get it, the comic (and its lapel button), the yellow words to the left, and the phrase "The Comedian" are a reference to the fantastic graphic novel Watchmen.


I couldn't figure out why no one was commenting on this aspect of the post, and all the effort that must have gone into its creation. Maybe folks are just so used to topfunky's blog posts being works of art (see the archives for more examples) that they've come to take it for granted.


Am I the only one who thinks adding more syntax is a bad thing?


It's fairly terrifying what an accretion of these sorts of options did to Perl, and I say that as someone who does a lot of Perl5 coding and enjoys it.

It would be one thing to just have a new symbol that was a drop-in alternative to what works now. But in this case you have a _sometimes_ alternative, so you need to remember a new rule, if only to be able read other people's code. That cognitive cost outweighs any keystroke benefit IMHO.


oh really?

Are you a Ruby dev?

If so, is your concern based on the impact this has on you personally, or is it based on concern for some mythical other dev who might have problems with the additional "cognitive cost"?

I've been a developing in Ruby now for about 4 years, I think this (the new syntax) is absolutely the right thing to do


probably not, but there's some cases where I think it's a good thing. This is one of those cases.


No, you aren't.


:) Matz eventually implemented my 2002 proposal. See http://virteal.com/RubyLanguage


This is one of the more upvoted Ruby stories I've seen on HN in some time, and it's a little bit disappointing. Sensationalism and inaccuracy are all it takes these days, eh?


I'm hoping that the story is upvoted more for the artistry of the post, The Watchmen reference and the plead to move to 1.9 and less for the argument of hashrocket vs. colon syntax.

But, given the chance HN will always fan the flames of the argument that lies within.


Though Ruby 1.9 itself doesn't agree ;-)

  [03:17:49 ~]$ irb
  ruby-1.9.2-p0 :001 > { a: 10 }
  => {:a=>10}


I prefer the "hash rocket" (I call it the fat comma), because it looks nicer when you have a hash with keys of varying length. Compare:

    {
        foo: 'bar',
        hello_world: 'OH HAI',
    }
to

    {
        foo         => 'bar',
        hello_world => 'OH HAI',
    }
If you try to do that with colons, it goes all wonky:

    {
        foo         : 'bar',
        hello_world : 'OH HAI',
    }


It's debatable whether aligning the mapping operator - be it a colon or a hash rocket - is a good idea to begin with. The point has been made in the past that if you edit the map so that the longest key changes, you'd have to realign all other entries too. For code versioning, e.g., in shared development, this creates unnecessarily large diff's. For instances, patches become more difficult to read, because they include lines that haven't actually changed content-wise, just layout-wise.


For code versioning, e.g., in shared development, this creates unnecessarily large diff's. For instances, patches become more difficult to read, because they include lines that haven't actually changed content-wise, just layout-wise.

Well, no.

Here's an example. File a:

    {
        foo         => 'bar',
        hello_world => 'OH HAI',
    }
File a with a long key added, let's call it b:

    {
        foo              => 'bar',
        hello_world      => 'OH HAI',
        longlonglonglong => 1,
    }
Then we do a diff:

    $ diff -uw a b
    --- a	2011-01-06 08:51:55.725595229 -0600
    +++ b	2011-01-06 08:51:52.285595452 -0600
    @@ -1,4 +1,5 @@
     {
         foo         => 'bar',
         hello_world => 'OH HAI',
    +    longlonglonglong => 1,
     }
So see, that's not a problem.


When I was looking at the page, I noticed that the example:

{

  key: "value",

  dr_nic: "The Comedian",

  ttl: 42
}

didn't seem to use the :symbols I know but omitted the colon.

Turns out, it somehow works:

> a = {my_key: "my_value"}

=> {:my_key=>"my_value"}

Has this always been there? It's kinda weird because that way they look like regular variables O_o


Cool. Now why doesn't PHP join the crowd and do this with arrays already?


I absolutely agree. The PHP array instantiation syntax is atrocious... I really dislike '=>'. While they're at it they might as well replace array() with [] and/or {}.


Yep, array() is the icing on the cake. Why is the most common data structure in the language instantiated with a function?

I could probably rant on PHP syntax all day long, but it's too off topic here.


It's technically a 'language construct', but it sure does seem like a function. Same with PHP's 'list'. It's not good syntax, in my opinion also. It doesn't even seem like syntax!


array() isn't a function, it's just PHP array literal syntax.

    $x = array("a" => 100); // legal
    $y = my_func("a" => 100); // illegal


Yeah, call_user_func('strlen', 'hello world') works but call_user_func('array', 'hello world') does not. But for ugly syntax, it's hard to beat the $ sigil being required everywhere for no reason at all (unlike Perl where it actually did something).


$vars remind me of programming mIRC.


You're right, I was a bit hasty and didn't consider that. It sure does look like a function though, where other languages typically use {} or something similar.


Let's throw in support for keyworded arguments while we're at it, too.


[NSArray arrayWithObject:notToMentionObjectiveC];


shudder

Having written and shipped 5+ iPhone apps written in the verbosphemy* that is ObjectiveC, I'm strongly looking forward to being able to do all future iPhone apps in a saner language like JavaScript or Python. If I do them at all. Ever again. In fact it's pretty much a hard requirement. Just after this current one ships.

(*: verbose + blasphemy)


To what end?


More elegant and easier to type syntax.


"This is your father's language sabre. A simpler syntax from a more elegant age."


I think they won't be removing hashrocket anywhere before 2.0, so you may use whatever you like better.


I think people are misunderstanding the title (which is not meant to be taken literally). They won't be removing the hashrocket ever, since (surely) they don't plan to limit hash keys to symbols only. The syntax only works in limited cases (namely symbols without spaces).


Which makes me wonder...why do it all if it is just for a limited case. Seems to make the code less understandable (to me) and it seems an unnecessary change to the language (to me).


Now if only you could change

list.sorting = {:name => 'ASC'} to list.sorting = {:name: 'ASC'}


I'm not familiar with the particular 'sorting' thing you're doing, but if the first things works, then the second should work in 1.9 if you use the correct syntax -- you have an extra colon. It should be: list.sorting = {name: 'ASC'}


Now if only more gems were 1.9 compatible.


I've been using 1.9 for a year now but I haven't come across any incompatible gems yet!



It's more problematic for those that include Ruby extensions in C, but fixing a library to make it 1.9-compatible is not generally that onerous a task (the major exception to this is string encoding, which is fine in simple cases but a complete nightmare in anything complex). If enough people just write patches for a couple of gems they use, everything would be 1.9-compatible soon enough. rvm lets you install all the different Ruby versions you'll need for testing easily enough.


Interesting to see this convergence of syntax. Unfortunately, it's not true that you don't have to learn a "new syntax." As often with Ruby libraries, the devil is in the subtleties.

This does not work (although I think it could):

{"foo bar": 'b'}

but this does:

{:"foo bar" => 'b'}

Without being truly JSON compatible, it's only moderately useful.

One annoyance of Ruby for me has always been that many libraries, such as Rails, allow for symbols or strings interchangeably - but only in "most" cases, often leading to head banging in the other cases.

More intrigue: the syntaxes are also mixable

{foo: 'bar', "add-a" => 'dash', :'or a' => 'space in a symbol'}

is valid.


Who would put a space inside the key name for an item in a hash?

I'm confident it's commonly understood best practice (among many languages at that) to use an underscore (_) in lieu of spaces in such things.

I agree that the quirks themselves are odd, but the matter of using a key with a space in it really stood out.


When your key is a phrase, for instance? A hash of { phrase => count } is a pretty common data structure.


It's really common, actually. A symbol can actually have spaces in it:

:"Some Text" is a perfectly valid symbol.

ruby-1.9.2-p136 :001 > h = {:"Some Text" => 123} => {:"Some Text"=>123} ruby-1.9.2-p136 :002 > h[ "Some Text".to_sym ] => 123


You can do it, but is it a good idea? What it implies to me is that at some point in your program, you're constructing symbols from arbitrary strings which aren't known ahead of time. This sounds like a recipe for disaster given that symbols aren't garbage collected.


Agreed. Also, { foo-bar: 'baz' } is not valid either. This is kind of a shame per the JSON argument as well.


syntax is rad, ask any pure mathematician.


lol aka javascript circa 1920


On the contrary, I wish javascript had more Rubyisms.

Don't you get sick of typing function(){..} when just {..} would do?


It sounds like you'd love CoffeeScript


I would love it if it the browser could run it natively. But if we're replacing browser language, can I just have plain Ruby?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: