Skip to content

Comment on Ruby's not ready

Comments

From an ex-Pythonista a year in to Ruby, point-by-point:

* Ruby's Unicode support is indeed inferior to Python's; unlike Python, you need to explicitly use library calls to work with UTF-16 strings. Python does indeed bake this in to the language.

* I don't use multiline regexps. On the other hand, I do use normal regexps, all the time, and the fact that I don't have to call out to a library to use them, or deal with special "match" objects with their own API, is a plus. Ruby has more natural support for regular expressions than Python, where regular expressions are an afterthought.

* Ruby's documentation sucks. Full stop.

* I have no idea why I'm meant to care about what this guy thinks of Ruby 1.9 or Ruby 2.0. There's a Ruby 2.0?

* Python is way faster than Ruby. Not by a little bit.

* Ruby's scoping rules are a dream compared to Python's; it support real closures, instance variables and methods with encapsulation, and coroutines that were designed for more than just iteration.

* Ruby and Python are equivalent when it comes to internal consistency. Python delegates virtually everything to a sprawling standard library for which there is, most certainly, "more than one way to do it". Calling out the difference between "print" and "puts" is particularly amusing if you know how Python handles the same distinction.

* Ruby has a cleaner object model than Python, where virtually every feature of the language appears to have been built by exposing the raw symbol table to the language. The simplest way to observe the difference is by taking an existing class and wrapping it with an object that catches all method invocations, a la "method_missing".

* Both Ruby and Python support keyword arguments by giving functions a dictionary type of keyword/value pairs; Ruby has the added benefit of a Symbol type, like Lisp and Smalltalk. No Ruby program fails to use them.

* I'm guessing about, oh, zero people on Hacker News give a shit about SAP support.

* I never noticed that Ruby didn't print the actual line of code that threw an exception. It's true! Python does! I never noticed that, either! That's pretty neat.

* Apparently, despite having been largely ported from Rails, Pylons is more mature than Rails. Awesome.

Scoping. I got bitten by a huge bug in ruby's scoping last night:

    irb> [1,2,3].collect{|n| n+1}
    => [2, 3, 4]
    irb> n
    NameError: undefined local variable or method `n' for main:Object # Expected behavior - n's scope is restricted.
    irb> n = 0
    => 0
    irb> [1,2,3].collect{|n| n+1}
    => [2, 3, 4]
    irb> n
    => 3 # BAD
I have ruby 1.8.4. Most recent version is 1.8.6. Gotta go see if it's a known bug or worse if it's supposed to work this way..

Edit: elided profanity.

It is supposed to work this way for some good reasons. But! Enough people have had the same problem as you that new versions of Ruby behave the way you expect.

Help me understand: what are the good reasons for the parameters of a block overwriting variables in an enclosing scope?

That is, we are talking about:

    i = 100
    x = 0
    [1,2,3].each { |i| x += i }
I understand why it is incredibly helpful for x to refer to a variable in the enclosing scope (even if we could accomplish this same effect with a fold). What are the use cases for the parameter i referring to a variable in the enclosing scope?

As someone who's used to Scheme (lexical scoping), I have to say that's pretty bizarre.

That's my second disappointment with Ruby blocks. The first was learning you could only have one block argument.

Do they resemble first-class objects at all? Can you assign a variable to a block? If you were to assign

  { |i| x += i }
to a variable outside that enclosing scope, and then used that variable in that scope, would i be overwritten in that call to the block?

"you could only have one block argument"

Do you mean one method can only have one block?

Having only read about Ruby and not coded in it, I'm not sure what the correct terminology is. I'm talking about doing something like this:

  someObject.someMethod({block1},{block2})

You can't pass more than one block literal to a method. But you can certainly pass multiple blocks; they're objects, just like anything else. Just add the token "lambda" to your expression:

   def foo(x, y); x.call(y.call); end
   foo(lambda {|x| 2+x}, lambda {1})  # => 3
It seems to me that in this case, the language degrades to approximately the same level as Common Lisp.

It's good to see that an upgrade to approximately the power of CL is possible in this case. Still, I feel a little let down. When I first saw Ruby block literals, I said to myself, "Hey, that's a great, concise syntax for lambda. Languages really are getting more lisp-like all the time." But now I know that Ruby block literals aren't really lambda. It does look like Ruby lambdas really are lambdas, though.

Blocks are lambda's, they're just not objects (but they can be converted to objects.)

If you can't pass more than one block literal to a method, then a block literal is not a real lambda, from the Lisp point of view.

This is confusing, because Lisp doesn't have the literal notation you're talking about. Since Ruby supports arbitrary lambdas using "lambda" notation (something Python does not support), it seems like Ruby's syntax can only be be better than Lisp's in this regard.

Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary. I do not think Ruby is better for having an additional syntax (block literals) that is just like an existing syntax (lambda) only more restricted.

The syntax isn't meant to be "a better lambda". The syntax is meant to recast other language functions, like looping and iteration, in terms of messages and anonymous functions. Since idiomatic Ruby basically never has "for" loops, despite their easy availability in the language, I'm going to go ahead and call the "block" expiriment successful.

If it's meant to recast certain language functionality in terms of anonymous functions, why didn't they just make block literals return anonymous functions? It still strikes me as an almost-could-have-been-great syntax.

Idiomatic Scheme basically never has "do" loops, despite their being part of the core standard, and it's been that way for decades.

I agree that block syntax should semantically result in expression evaluating to a Proc object. This would be great:

  y = {|x| x} # => #<Proc>
I agree that block syntax could therefore be greater than it is.

I disagree that you can fairly knock Ruby for this, since blocks and implicit block arguments are still better than what you get in other languages.

Could you give a more complete example? I'm getting sane behavior in Ruby 1.8.6:

  irb(main):010:0> f = lambda { i = 100; x = 0; [1,2,3].each { |i| x += i }; x}
  => #<Proc:0x05011b34@(irb):10>
  irb(main):011:0> f.call()
  => 6

you missed the fun:

    macbook-pro-2:trunk raganwald$ ruby -v
      => ruby 1.8.6 (2007-03-13 patchlevel 0) [i686-darwin8.10.1]
    macbook-pro-2:trunk raganwald$ irb
    irb(main):001:0> lambda { i = 100; x = 0; [1,2,3].each { |i| x += i }; i }.call
      => 3

Oh, you're right, I had missed that. Yeah, that's dumb. I can't think of any iterators that would leave useful information in the parameters, either.

Yes, it is a known problem, and I have also found myself editing my own profanity:

http://weblog.raganwald.com/2007/02/why-ruby-is-not-acceptab...

Your "bug" is lexical scoping?

No, it's lack of consistent lexical scoping.

Either ruby blocks create a new scope or they do not. Which is it?

They create new scopes, and close over the their enclosing environment. In other words, it supports both block-local variables and closures.

I don't understand. You mean there's a good reason for it to overwrite variables in outer scopes if they exist?

Is there any way to specify if I want block-local or not?

The way python deals with scopes is to provide read access to outer scopes if there are no writes to a variable.

Of course there is: if you couldn't access variables in the enclosing scopes, blocks would be drastically less useful. Ruby uses anonymous functions where Python would use a loop construct.

[We took this conversation offline. it seems this is a known gotcha (e.g. http://www.osix.net/modules/article/?id=401), and may be changed in ruby 2.0 (http://groups.google.com/group/sbonrails/browse_thread/threa...). Thanks tptacek.]

So why are you still using Ruby?

I am only recently trying it out, and it seems to me Rails gives the line number of errors.

Today, I'm writing in Python, because Python has interfaces to the Win32 COM object for WinDbg's KD.EXE, and trying to make that interface work in Ruby cost me a day of x86 runtime code generation.

I'm not hating it, because despite all the stupid arguments, Python and Ruby are basically the same language.

But why do I still use Ruby? Because builtin Unicode doesn't matter to me that much, I can often answer questions from the Ruby source code faster than I can from Python's hard-to-search documentation, evented code in Ruby and Python is equivalently fast and everything else I do in C, and none of the other complaints from this article were valid. Against those points, Ruby's block construct is a huge win.

Saying Pylons has 'been largely ported from Rails' is inaccurate, this only really applies to the webhelpers.

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.