Home > Programming > Ruby post-Python: second impressions (or: how I learned to stop worrying and love the implicit)

Ruby post-Python: second impressions (or: how I learned to stop worrying and love the implicit)

So, it’s three months since I wrote Ruby post-Python: first impressions. I’ve got a few small things to say, and a few bigger things. I’ll start with the small things:

  • I was worried before about the change from elif to elsif, but it turns out this hasn’t been a problem. Having a decent case statement means I haven’t had to write a single elsif since I’ve been using Ruby.

  • The case statement is AWESOME. Being able to match against ranges, regexes etc. is just pure genius.

  • I said about parsed and unparsed strings before – at that point I didn’t know about string interpolation. More awesomeness.

  • I’m still on the fence with 0 not evaluating to false, but I can see more and more why you’d want it. For example, instead of saying if myArray.length you’d use if myArray.empty? which is obviously more readable.

  • Question marks in method names are brilliant. I first came across the idea in Scheme, and I love it.

Implicitness (implicity?)

So, here’s my main second impression. It’s all to do with the implicitness of the two languages.

In Python, there’s a preference for explicitness. Indeed, line two of The Zen of Python reads:

Explicit is better than implicit.

Ruby is very different in this respect. Ruby’s syntax is full of implicitness. One good example is self in methods. Every method in Python needs self as its first argument (I know it’s only called self by convention). And when you call a method inside an object, you call it on self. In Ruby, the self is implicit, both as an argument and as the receiver of the method call.

I’m not going to get into an argument here about which is better. My current thinking is that requiring self as an argument to every method is completely redundant, but I quite like the clarity of using self as the receiver of all internal method calls. It still makes me nervous not prepending self. to method calls in Ruby – they look like global function calls to me. I’m getting used to it though.

Another example is method invocations. In Python, you need the parentheses. In Ruby you don’t. This gives an advantage to both languages. On the Python side, it means that myObj.meth returns the method, whereas myObj.meth() returns whatever meth() returns. That gives handy functional expressivity. I use this all the time in JavaScript, where it’s essential to know the difference between func and func().

On the Ruby side, the ability to call methods without parentheses has some really nice consequences. Coupled with method_missing, it gives the ability to write really nice internal DSLs. And because direct access to object attributes is impossible, you’re able to write code that looks like direct attribute access with all the benefits of accessor methods. That is a huge win for me. Nobody likes having millions of getters and setters, but if the alternative is having to rewrite all your client code when you realise you need to do more than simple assignment they’re a necessary evil. Thanks Ruby for helping me avoid that!

Something else that’s nice with Ruby’s implicitness is the fact that arrays and hashes don’t need brackets and braces. I mean, what else could :a => 5 be other than a hash? The syntax is unambiguous, so there’s no need to insist.

Special cases

Here’s another line from The Zen of Python:

Special cases aren’t special enough to break the rules.

It strikes me that this is another big philosophical difference between the two languages. In Python, whatever you’re doing, you’d better be doing it consistently and by the rules. It’s rare that special syntax is added to do something that could be achieved with normal code. Ruby, on the other hand, tends to optimise for the common usage, which might mean in certain cases a different syntax may be needed. Eli Bendersky, in his excellent blog explained this philosophy in his description of Ruby’s blocks:

… there is one common case that stands high above all other cases passing a single block of code to a method that makes something useful out of it, for example iteration. And as a very talented designer, Matz decided that it is worthwhile to emphasize this special case, and make it both simpler and more efficient.

Is it worth keeping all syntax consistent, or is it OK to introduce special forms for certain common circumstances? Before I started using Ruby, I was probably in the former camp, but I’ve been convinced of the advantages of the latter now.

Metaprogramming

Every time the question of differences between Ruby and Python come up (or more often, the issue of superiority), the Pythonistas insist that all the metaprogramming magic possible in Ruby is possible in Python as well. But the fact of the matter is, it’s just not used that often in Python. With Ruby though, metaprogramming is just what’s done. In a lot of ways, this goes back to the implicit/explicit divide, in the sense that dynamically created methods aren’t visible in your source code. Personally, I don’t see the problem with that, but I can see why this would upset others.

What does Python have?

It’s obvious that I’ve fallen for Ruby, and Python’s lost its shine a bit for me. But it’s worth acknowledging the bits that Python does well. Interestingly, these all seem to be related more to the community than the language (although the language selects for the community pretty heavily I guess).

  • Solid libraries, especially NumPy/SciPy. There’s not really an equivalent in Ruby.

  • Stable libraries. In Ruby, and especially Rails, the best way to do something today will be the old way to do it tomorrow. There’s always a better way to do it, and it can be difficult to keep up. Because most of my experience in Ruby has been in Rails so far, it’s hard to say whether this is actually just a feature of the Rails community.

  • Mature community. This might be controversial, but it feels to me like the Python community likes to just get stuff done, whereas the Ruby community wants to be playing with the newest, shiniest things out there.

  • Decent GUI. By this, I mean PyQt. QtRuby looks like it’s miles behind PyQt, and FxRuby doesn’t look very advanced. PyQt, however, is great, even if there is a bit of impedance mismatch between a C++ framework and Python.

Categories: Programming Tags: , ,
  1. October 17th, 2010 at 03:06 | #1

    arrays and hashes don’t need brackets and braces. I mean, what else could :a => 5 be other than a hash? The syntax is unambiguous, so there’s no need to insist.

    This is a little misleading. It is only within method calls that you can leave the curly braces off of hashes. As for arrays, I assume you mean passing in a list of arguments to a method which takes a variable length parameter. It seems like a stretch to say that means arrays don’t need brackets.

    And => is also used in rescue clauses.

  2. Jerry Kindall
    October 17th, 2010 at 07:01 | #2

    Python has plenty of “syntactic sugar” for special cases. Off the top of my head, there are list comprehensions, context managers for the ‘with’ statement, and decorators. Even interpolation as an operator qualifies. Perhaps Ruby has more (I have only a passing familiarity with it, but the fact that it has a `case) but Python is hardly innocent.

  3. October 17th, 2010 at 08:11 | #3

    Great set of comparisons. Thanks for the write up.

  4. masklinn
    October 17th, 2010 at 09:23 | #4

    Is it worth keeping all syntax consistent, or is it OK to introduce special forms for certain common circumstances? Before I started using Ruby, I was probably in the former camp, but I’ve been convinced of the advantages of the latter now.

    You’ve been incorrectly convinced I fear. Consider a third option of making blocks first-class objects, therefore having:

    a = { 'foo' }
    

    instead of the current

    a = Proc.new { 'foo' }
    

    and

    a = { 'foo' }
    

    being a syntax error.

    All of a sudden, you can easily provide multiple blocks to a single method or even call methods directly on blocks ({ a < b }.whileTrue { puts 'Free while' }), the language is more consistent, simpler and better handles complex cases, letting you build more control structures. It also removes the magical special cases of the ampersand in both parameter (a block is a normal param) and method call (a block is a normal param). Better, that also removes the current need for block_given? and yield.

    You get rid of 3 or 4 (depending on whether you get rid of sym#to_proc, which I’d probably recommend) constructs in the language *and* you get a more general and flexible feature, what’s not to like.

    That’s what Smalltalk does, and as a result not only doesn’t smalltalk need a for, it doesn’t need a while or a if statement either.

  5. masklinn
    October 17th, 2010 at 09:31 | #5

    Damn, I’d missed a lot of incorrect stuff, I have to go but I can still do at least one:

    And because direct access to object attributes is impossible, you’re able to write code that looks like direct attribute access with all the benefits of accessor methods. That is a huge win for me.

    Hello, you can do that in Python. There is @property (and more generally descriptors) for explicit virtual members (methods used as attributes), and __getattr__ for the (rough) equivalent to method_missing.

    Something else that’s nice with Ruby’s implicitness is the fact that arrays and hashes don’t need brackets and braces. I mean, what else could :a => 5 be other than a hash? The syntax is unambiguous, so there’s no need to insist.

    I’m pretty sure arrays do need brackets:

    > irb
    >> 1, 2, 3
    SyntaxError: compile error
    (irb):1: syntax error, unexpected ',', expecting $end
    1, 2, 3
      ^
        from (irb):1
    

    In fact so do hashes outside method calls as far as I know (though I might be wrong), this is (another) special case of Ruby, which in this case serves to emulate Python’s kwargs. Except it doesn’t look quite as good when forwarding a dict and adding new keys to it. And it has an other annoying issue (not sure whether that’s been fixed in 1.9) of not being able to provide both va_args (*foo) and keyword arguments to Ruby methods (or not in a sane way anyway)

  6. Adam
    October 17th, 2010 at 10:30 | #6

    And it has an other annoying issue (not sure whether that’s been fixed in 1.9) of not being able to provide both va_args (*foo) and keyword arguments to Ruby methods (or not in a sane way anyway)

    In ruby 1.9:

     def f(*a)
         a
     end
     arr = [3,4]
     f(1, 2, *arr, :a=>"one", :b=>"two") #=> [1, 2, 3, 4, {:a=>"one", :b=>"two"}]
    
  7. masklinn
    October 17th, 2010 at 10:55 | #7

    @Adam

    OK it hasn’t changed, it still isn’t sane/clean, as in 1.8.

  8. October 17th, 2010 at 11:06 | #8

    Decent GUI

    I’ve made a little framework that hopefully will help reduce the divide. It’s called Freightrain and you can find it here => http://github.com/bolthar/freightrain

  9. Dag
    October 17th, 2010 at 19:21 | #9

    For example, instead of saying if myArray.length you’d use if myArray.empty? which is obviously more readable.

    Python:

    if not my_array: ...
    

    Every method in Python needs self as its first argument

    This makes scope obvious and nothing special. It also works well with multiple inheritance, just pass our self to a parent’s method. It lets us reference instance methods without having the instance, for example map(str.upper, ['list', 'of', 'strings']).

    The case statement is AWESOME

    case = lambda x: y == x
    if case(a):
        pass
    elif case(b):
        pass
    

    Being able to match against ranges

    >>> 2 in range(10)
    True
    

    You could simulate Ruby’s === (what it uses for case matching) as a generic function in Python. Though I find it a rare need to do different kind of matches in a case switch.

    There’s a lot of smell in Ruby too:

    • The global namespace (require etc)
    • The modules are separate from the import system, syntactically ugly if you ask me (CamelCase::), requires an extra indentation level but are highly needed due to the above point, only way to do multiple inheritance
    • Inability to selectively import parts of a file
    • Block heavy DSLs are imperative rather than declarative leading to confusing APIs compared to Python’s decorators which almost exclusively return a function.
    • Subjective perhaps but I think Python’s focus on attributes leads to prettier and better code than Ruby’s focus on methods

    Been a while since I thought about this so I probably forgot some points.

  10. barfoo
    October 17th, 2010 at 23:23 | #10

    The use of self in standard python methods also makes classmethod and staticmethod decorators easier to work with.

  11. October 19th, 2010 at 03:36 | #11

    @masklinn

    Yes it has changed in 1.9, hash args work a lot better with var args now:

    def hello(*args, options)
      puts args
      puts options
    end
    
    hello(1,2,3,4,5, name: :masklin, age: 37)
    #=> [1,2,3,4,5]
    #=> { :name => :masklin, :age => 37)
    
  12. andrew
    November 14th, 2010 at 00:03 | #12

    both are great languages, I’ve use both and I feel than ruby is python with steroids…It’s so great but so young too….

  13. August 25th, 2013 at 10:24 | #13

    Thanks to my father who shared with me concerning this website, this blog is actually awesome.

  14. October 18th, 2013 at 10:05 | #14

    I’m excited to uncover this great site. I wanted to thank you for ones time just for this fantastic read!! I definitely liked every part of it and I have you saved as a favorite to see new things in your site.

  15. November 27th, 2013 at 05:33 | #15

    Excellent post. I was checking continuously this weblog and I’m inspired! Extremely useful information particularly the last phase :) I care for such information a lot. I used to be looking for this certain information for a long time. Thanks and best of luck.

  16. November 27th, 2013 at 11:00 | #16

    Have you ever thought about including a little bit more than just your articles? I mean, what you say is fundamental and everything.

    But think about if you added some great graphics or video clips to give your posts more, “pop”! Your content is excellent but with pics and video clips, this website could undeniably be one of the most beneficial in its niche.

    Excellent blog!

  1. October 17th, 2010 at 02:54 | #1

Comments parsed as Markdown.