Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Why I Like Ruby (wekeroad.com)
57 points by robconery on July 16, 2010 | hide | past | favorite | 43 comments


One thing that puzzles me is the "expressiveness" factor. When I read code, in any language, I translate that code into blocks / models, not English. I don't read the code as I would read a book or an article. That would take too much effort / time because right after "reading" it, I would still have to parse it into the mental model.

For me, if you write "if (myvariable != null) { }" or "unless(myvariable.nil?) end" doesn't make a difference. In fact, if I were to read it as English "if my variable is not null do" sounds better than "unless my variable is null, question mark?" -- I'm actually puzzled how you would "read" that.

In any case, my argument is that a translation from code -> mental model is more efficient than code -> English -> mental model, in which case expressiveness is only helpful in as much as it helps in parsing the code and not "reading" it in English.


The given example of "unless myvariable.nil?" is a little bit awkard. In fact, the use of the "unless" keyword is, in my opinion, almost always awkward unless it's postfixed: "do_a_thing unless wait?"

Not only that, but most of the time, the expression could simply be written "if myvariable; ...; end", seeing as the vast majority of the time, we only care if the thing we're testing is falsy.

Ruby does give you the tools to be very expressive, indeed almost conversational in your coding style, but it also gives you enough rope to hang yourself.


Damian Conway in Perl Best Practices recommends not to use negative control statements at all (unless and until).

So this would be a no-no:

    unless ($this > $that) {
        do_something();
    }
He's happy they're used to postfix statements however he says that these should only be used for flow control. For eg.

    for my $i (@list) {
        next unless $i % 2 == 0;
        do_something();
    }


Correction: Conway says don't use postfix unless, for, while or until

So last example should be:

    for my $i (@list) {
        next if not $i % 2 == 0;
        do_something();
    }
Personally I don't follow the bible to the letter :) I do use prefix & postfix unless but only with simple truth conditions like this:

    unless ($this) { ... }

    next unless $this;


Expressiveness is important. I remember many times reading logic and missing the little tinsy ! before a giant expression not making the quick flag in my mind raise indicating that this is a unless, not an if. In ruby the idea of the unless is not to save typing. In fact if !foo is easier to write than unless foo. However after a tad bit of getting used to it, you realize that it becomes immediately obvious when skimming the code what the operation is doing.

The idea is lets leave the brainpower for the hard problems, and not require you to use all that brainpower to figure out the plumbing.


Actually the ruby would just be "if myvariable" - I think it's more readable simply because it expresses the minimalist core of what that line of code is doing. I could also use Blub and say "IfConditionIsTrue(myVariable.booleanValueOfNullStatus())"

Yes, you're right that my mental model of the 'if' construct will be basically the same in any language, but it's fundamentally less work and less error prone for me to translate ruby than some other language into that model, and the same is true going the other direction


Let me just translate that post:

  include 'rspec'

  your_point.disagree do
    the_example.in_ruby = "if myvariable"

    me.think do
      this_thing(the_example.in_ruby).is(:more_readable)
    end.because do
      that_thing.expresses(the_example.in_ruby.what_its_doing.minimalist_core)
    end

    me.could do
      using "blub" do
        self.say "IfConditionIsTrue(myVariable.booleanValueOfNullStatus())"
      end
    end.implying do
      self.last_statement.is(:crazy_talk)
    end
  end

  your_point.agree do
    constructs = languages.map do |language|
      in language do
        me.mental_models.find_by_name("The If Construct")
      end
    end

    constructs.should be_identical

  end.with_caveat do
    languages.min do |language|
      in language do
        me.mental_models.find_all
        return me.mental_strain
      end
    end.should == "Ruby"
  end


Brilliant! Except at the end...

end.should be "Ruby"


Passing blocks is a much bigger deal to me in terms of expressiveness. Being able to pass around blocks lets you write APIs that are as powerful as language features. The new Routes API in Rails 3 is a great example of this:

  resources :people do
    resource :avatar
  
    member do
      get  :republish_deep_links
      post :resend
      put  :accessible_projects, :upload_preference
    end
  end
That code sets up various URL paths in a Rails app. It maps URLs like /people/george/resend to the proper controller in the MVC framework.

In many other languages, you could set up an API like this, but instead of taking a block, the "resources" function would have to accept some sort of nested data structure. That gets you pretty far, but what about when you want to do something that the parser of that data structure didn't expect? Usually you can't.

But in Ruby, code is data and you can do whatever you want in those blocks. You could do this, for example:

  resources :people do
    resource :avatar

    member do
      ["apple", "orange", "peach"].each do |fruit|
        get "eat_" + fruit
      end
    end
  end
That would map /people/george/eat_apple, /people/george/eat_orange, and /people/george/eat_peach.

And maybe that's not that impressive to you, but you can inject arbitrary code in there. That's what people mean when they talk about expressiveness.

Examples of this are everywhere in the Ruby world. Check out HAML and assert_select.

APIs + Rigid data structures = Pain and suffering

APIs + Ruby blocks = Flexibility and power


I should say the first example I gave is from DHH's awesome presentation on his favorite Rails 3 features: http://www.youtube.com/watch?v=b0iKYRKtAsA


I find that

    display_warning() if problem_condition() 
feels much more expressive in some situations than

    if(problem_condition) display_warning()
The later just emphasizes the primary activity to me - again, in some circumstances. It's something I miss now that I'm not doing Ruby anymore.


That breaks efficient code reading. When you read code, you tend to skip through basic-blocks (i.e. consecutive statements without branches) and follow them to that intersection point where a decision has to be made. In traditional form, if you have:

  if (condition) consequence();
You're able to "run" the condition in your head and step into the consequence without loss of pace. But when you put the consequence before the condition, you will have to backtrack.


unless the consequence itself is something that signifies the end of a basic block, like a break or a next or a return.


  backtrack = true unless consequence.signifies? :block_end
It doesn't seem that much harder to read.


Unfortunately though you are potentially adding subtle bugs here which can only be caught at run time.

To avoid excessive test coverage it would be more pragmatic to do:

  backtrack = (consequence.signifies? :block_end) ? false : true
But that's not as easy to read :(


I didn't reply to that one-liner because it is a one-liner.

A lot of software engineering lore crumbles into tautologies or is completely obviated when you're working at the microscopic level.


My thoughts as someone who's been full time on C# for the last 5 or so years:

Case 1 - His examples are toys, I don't think there's really an argument here. Better examples might convince.

Case 2 - This is less about the language and more about the tools and the culture. The ecosystem in C# land is focused much more heavily in the business / enterprise direction which personally I hate. Point to Ruby.

Case 3 - Half a point maybe. The .Net library is actually pretty good and each version of C# brings the language a step closer to the sort of concise code he shows. You can certainly get very close with a few wrappers here and there.

Case 4 - Meta Programming, oh yes. 10 points to Ruby. It has been said elsewhere that C# doesn't have meta programming because it was designed to be predictable for whoever has to read it. In practice this seriously restricts our ability to express certain types of concepts that don't fit the basic OO model. Aspect oriented programming and mix-ins are the two currently frustrating me.

We try to tackle this with all sorts of hacks (e.g. PostSharp, mangling .dlls after compilation) but they don't fit well with the rest of the tools.

This means lots of boiler plate repetition we can't kill. Microsoft likes to use code generation for this which I also hate.


C# has some meta-programming with the addition of closures and good polymorphism. Check out how http://kayakhttp.com/ uses them to build a fairly sophisticated async web server.


I skimmed part of this, and had to giggle a bit at this quote (of a quote):

"It's clear that C# isn't the best language to use for Web Development. Just ask the guys who are writing the frameworks with it."

Sure C# is verbose. But I still feel it's pretty expressive. I'm working on a web framework in C# (http://github.com/ecoffey/Bebop). Granted it's totally a toy thing, but still. Looks beautiful to me :-P

Also it annoys me he compares non-idiomatic C# with idiomatic Ruby.

I guess I should quote this FTA: "My whole point here is to tell you why I like it - not to convince you of anything.", and finish with it all comes back to the right tool for the job shrug.


Beyond the verbosity, my issue with C# (and even worse, Java) is that they're no good at what the web needs. The staticity gets in the way, it means the language has a hard time bridging before the "everything is a String" world of the wider web and the internal "everything is such and such type". These languages are hard (especially with generics) where the web is soft. In Python or Ruby, you can just deserialize JSON to native types, drill down your dicts and lists as needed and call it a day.

In C# or Java, you'll have to define exactly how your dicts nest and everything (or give up type safety), and that means you have a much lower flexibility.

Finally, C-inherited Java are dreadful at string munging. Which is understandable, they don't really like strings after all, but again the wider web deals with strings a lot (if not almost exclusively), that means one more clash.


The verbosity of both C# and Java just appall me.

It would seem to me that verbosity and expressiveness are opposites - a language which inherently uses many lines to express an action is a language which inherently expresses little per line. How could it be otherwise?


I realize I'm going outside the context of the discussion here, but just wanted to comment on this.

> a language which inherently uses many lines to express an action is a language which inherently expresses little per line

Depends on what you an action is. It's nice working in a language where each thing does one thing and one thing only. A single line that does numerous things at once can get pretty crazy, with magical processes spreading pixie dust over your data.


OK OK - edited: Rubyists are very stringent about syntax style, and there are styles WRT to C# but I'm not altogether aware of how you would accomplish the same things that I set out to do.

Specifically: a null check.


Personally I like the andand gem for this..

my_variable.andand.do_whatever


For a web designer that works with css, html, and php, the only thing keeping me away is deployability. It seems that without learning git and the ways of my terminal, I have no way to make a web app with RoR. Not that I don't want to learn. I do, badly. But I just wish the barrier to entry could be lower. Either that or I just need to find a better tutorial going from nothing to a very simple app on Heroku and showing me how to experiment with it.


Deploying Rails has gotten significantly easier since the release of Passenger. It may not be quite as easy deploying PHP, but it's not far off.


Having done both PHP Zend Framework deployments, which require mod_rewrite rules in each vhost, and Passenger deployments, which only require touching a text file to do restarts, I much prefer Passenger deploys.


You don't need Git to use RoR ... but try and learn it. It's awesome ;)

Git also comes with a SVN bridge, which works wonderfully for me.

Also ... Rails has its own development web-server. You don't need to have a production environment to learn it.

And I really recommend that when learning about any web framework, in any language ... just learn how to deploy a production server by yourself. It saves a lot of pain later on ;)

Not to mention that I wouldn't use Heroku ... sure they provide a valuable service, but personally I get a cheaper deal by getting EC2 instances and configuring them myself. It's hard the first time, but then you can scale your infrastructure easily ... EC2 is wonderful in that regard.


Do you have any guidance or suggestions for reading material for how to migrate from using Heroku to going directly to EC2, for someone who is somewhat new to sysadmin?


http://railstutorial.org is what I used to learn rails. It have information on how to use git and heroku too.


I would recommend you skip rails and go for Sinatra. I found that rails had too much for me to learn to find it useful, a bit overwhelmed by the convention V's configuration thing. That is the rails convention didn't really match my own.

Sinatra doesn't have all that. A combination of Heroku, Sinatra, and DataMapper (nice, cleaner, simpler alternative to active record) and what you have is a very easy to deploy application.

I'm doing the above, and doing it on Windows (sorry, I'd love to use Linux but my wife uses this machine when I'm not and has some apps that are not replaceable, and I don't want to mess around with dual booting. So when I get FU money I'll be Linux all the way).

If you have any questions I'd be glad to help.


I second the Sinatra recommendation. When I tried rails for the first time, it felt very large and unwelcoming, with a bunch of files that I didn't create sitting in my directory hierarchy doing unknowable things. Sinatra behaves just like any other library and requires far less of a time investment to pick up and start using.


Well, rails is opinionated software, and one of those opinions is that you should probably be familiar with a terminal.


go through the articles slicehost has written up to get rails and passenger installed and your app deployed. They are fantastic and you'll be very comfortable doing it anywhere else afterwards. http://articles.slicehost.com/rails


You don't have to use git if you don't use heroku. Passenger is an easy way to deploy rails code on your own.


I am still not sure how unicode works in Ruby. Therefore I never dare to do any String operations like regular expressions or the the chomp/split he does.

The pragmatic book mentions something about kcode (I think), but Rails seems to use something else. Back when I last tried, I couldn't find any definite answers to this.

Maybe Ruby 1.9 solves this problem - but is it ready yet? Again, last I tried, there wasn't even a MySQL driver for it.


To be fair unicode has never been without issues in Perl & Python either! Perl has generally been the best of the three (IMHO) but Python 3.0 & Ruby 1.9 have probably now caught up.


  raise "Hey dude! Where's My Variable Assignment" if my_variable.nil?
I really hate raising an exception like that. I am surprised Rubyists like it like he says they do.


I prefer:

    my_variable or raise 'Hey dude! Where's My Variable Assignment'
or else:

    my_variable.nil? and raise 'Hey dude! Where's My Variable Assignment'
since it puts what's being tested in front like with if/then but is also syntactically light-weight.


I could be completely wrong - I do like the "or" syntax as well - reads much better.


Well explained. However many tools of ruby. Tools that we as good developers carry at the bottom of our toolbox for those rare cases where they make life a billion times easier, can also be tools of spaghetti. One of these is the ability to inject behaviors into a class. Its like adding to Array.prototype. The point is that just like a goto statement, it has a few legitimate uses, but most of the time leads to spaghetti code. I think every language has this, and there should be big flashing warnings when tutorials show this methodology stating that "use this only where appropriate, and it is usually only appropriate in these situations:..."


[dead]


DHH invented Rails. Yukihiro Matsumoto invented Ruby.


Also Al Gore!




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

Search: