raganwald
(This is a snapshot of my old weblog. New posts and selected republished essays can be found at raganwald.com.)

Tuesday, July 04, 2006
  Six things I also dislike about Ruby (and four that I don't)


10 Things Elliotte Rusty Harold "Hates" About Ruby:
1. initialize

To create a new object in Ruby you invoke the new class method like so: c = Car.new("Toyota") Naturally you think the constructor would be defined in a method called new, right? Or just maybe a method called Car like in Java? But no, instead it has to be called initialize.
Right on. That's annoying.


ruby , originally uploaded by Victoria Wang.
2. Weak typing
Ahhh... I have promised not to write the same essay more than four times in any twelve-month period. Let's agree that Ruby would be even better if there was some sort of type inference system that doesn't require type declarations everywhere.
4. Global variables

Didn’t somebody tell me Ruby was an object-oriented language?

5. puts

Do we have to copy C’s most ungrammatical, pointless method names?
Matz was clearly in Raymond Chen mode when he put all the perlisms in the language. As an application developer, I don't use any of them. But I'll accept it on faith that there are people using Ruby as a scripting language who like them.

But I don't have to like them.
6. Zero-based arrays

C arrays began at zero to match memory addressing. In a weakly typed scripting language, there’s exactly no reason to do this. There hasn’t been for decades. Can’t we please start the arrays at 1 where God intended? Hasn’t anyone read Numerical Recipes? Aren’t we tired of fencepost errors yet?
See above. But with extra pursing of the lips. Zero-based arrays are a perfect example of a leaky abstraction. Back in the days when programmers all wore beards and C was the new kid on the programming block, zero-based arrays were really just a funny kind of macro for performing a multiply and add on an integer that you hope is a pointer.

No really, it's true. Go ahead, look it up. I'll wait.

Okay, I agree with Rusty and then some. I'm perfectly okay with a new language using 1-based indexing. I think I can get over the momentary unfamiliarity as I untrain my brain to stop doing this unnatural zero-based thing.
7. elsif

Sorry. “elsif” is not a word in the English language, and it shouldn’t have been one in Ruby. Would typing one extra letter have been so onerous?
Getting this wrong is almost a rite of passage when learning Ruby. Annoying. But not fatal, fortunately. At least Ruby doesn't assume elseif is a new variable declaration. I hope.
8. $_

Global variables suck. Did I say that already? Global variables you didn’t actually create and that pop up out of nowhere suck even worse. And global variables whose names are indistinguishable from line noise suck still more. Java’s verbose, but that verbosity is in the service of legibility. Ruby’s compactness all too often serves only to obfuscate.
See above, again. I really suspect that the Perl-style stuff is of value when writing one-liners on the command line. It probably doesn't belong in an application longer than a page.

I would normally say "so don't use them," but there is a small problem with globals: at least one, $=, affects the default behaviour of a kernal function: regular expression matching. For that reason, I consider it a little dangerous.

I can live with built-in global variables that are, essentially, syntatic sugar for functions. But I'd rather that they not be syntatic sugar for method calls that change global state. No doubt you cannot have one without the other and thus I dislike them as well.

Now about the lack of object orientation. It looks like other langauges have exactly the same problem. What's with all the singletons out there? And what happens when you call java.lang.System.setProperty(...)?

It seems that programmers like this stuff, no matter how bad it is for them. Which reminds me, ribs for lunch today?
9. Methods are public by default
I personally don't care one way or the other about what the default access is, but I think Rusty has been perfectly cogent when presenting his argument in favour of restricting APIs as much as possible. And he goes further to suggest that restriction should be the default.

Shrug. After all, you can choose what you want. Is there something drastically wrong with assuming that if a programmer writes a method with the default (public) visibility, that the programmer knows what she is doing and wants it that way?

Uh, oh. I think I've also written this essay before. Let's move on.

But I'll put this one down as a tie. I think Ruby would be a perfectly fine language if you had to take more care to make methods public.

By the way, is there a resonance between this perspective and with the way you can declare that certain Rails controller actions must be invoked with PUTs or via XmlHttpRequest?
10. No package access or friend functions.

There’s no access in-between public and private. All too often different classes in the same library/application/codebase need more detailed access to their other parts than the general public should have. Ruby provides no way to manage this.
(Something I find quite interesting: most of the Java programmers I have met do not use Java's 'default' visibility. But back to the subject at hand.)

There are a lot of security measures you have to put in place if your objective is to hobble programmers. Even then you may find they are motivated to work around the constraints you are attempting to impose:
Just about every developer at some point has used a library or a component developed by someone else. Just about every developer at some point has also gotten frustrated with the library to the point of being willing to find the guy who decided to make a method private and talk some sense into him. Well, most of us wouldn't go that far, but it would certainly be nice to be able to change things that make our lives miserable. It's not that libraries are written by mean people; it's just that even the brightest designers are unable to foresee all the possible ways that other developers would want to use their code.
--Hacking Java Libraries
In addition to providing various access constraints, you also have to put together security mechanisms like enforcing sealed packages. Within the Java world, researchers are also proposing sealed classes and even sealed methods.

Now I'm not going to say that just because someone can hack around your mechanisms that the mechanisms are bad. I'm perfectly okay with a language that gives you nothing better than a glorified annotation, the equivalent of a post-it note on your ham sandwich saying "mine! don't touch!!"

It's useful to document your intentions, even if perfect enforcement is elusive. That being said, I'd like to close with the wise words of another: Less is More. For some people, fine-grained access control is important. But adding that feature is not free.

Given the current culture of Ruby programming, I'd say that additional access control would be expensive if you go down the rabbit hole of trying to stop people using Ruby's meta-programming strengths to hack around your restrictions.

However, lightweight mechanisms for access control and managing name spaces could be useful.

Conclusion:

As I suspected before I wrote this, I agree with more than half of Rusty's observations. Isn't this always the way? Most people have far more in common with each other than not.

One of the reasons I read Rusty's work so carefully is that he has a markedly different viewpoint than my own. It's nice to read all the nice affirmation from people who seem to value the same things I value, but there is always an opportunity to learn something profound when looking at the same things from a different perspective.

Labels:

 

Comments on “Six things I also dislike about Ruby (and four that I don't):
*sigh*
Car.new() vs new Car();
is just because you're used to saying new Car(); from prior experience.
Why do you want programming languages be equal - when their purpose and existense hints they're not.
 
Okay, let me be more clear: I find initialize(...) (or as I write it in lisp-ish Ruby, (initialize ...) annoying.

Now about Car.new(...) vs. new Car(...): new Car(...) is broken. The new operator is bad unless you can override it.

In a language like Java where the new operator is one of those things that we programmers aren't considered grown-up enough to play with, new Car(...) is not polymorphic and always allocates a new object.

An example of where this is wrong is writing a connection pool. You can't say "new PooledConnection(...)" and have the PooledConnection class give you an unused connection from the pool.

I'm not saying I prefer Car.new(...) or anthing else. I understand that creating a new Car is a class method and that initialize is an instance method. But I do not find it intuitive or obvious that initialize is the magic method that always gets called when you create a new Car.

p.s. I do not want languages to be equal. Does my writing suggest that I do?
 
(Something I find quite interesting: most of the Java programmers I have met do not use Java's 'default' visibility. But back to the subject at hand.)
I remember using (abusing?) this feature to get around an installshield 4java design decision, it was quite nice to just declare my class in their namespace and immediately be able to access the details of their classes to write my project file->text converter.
 
it was quite nice to just declare my class in their namespace and immediately be able to access the details of their classes

Hee hee! How ironic that the one message defending java programmers knowing and using the language's features comes from someone working around a language feature.
 
Took me a while to get used to, but I ended up liking it. new() is the constructor, initialize() is the initializer.

If I want to change how the object is initialized, I mess with initialize().

If I want to change how the object is created (singleton, connection pool, lookup, etc), I mess with new().

It's a nice separation that keeps the crazy factories away.


Object oriented languages are about message passing. Some think globals are considered harmful. Some think writing too much code to fake globals is harmful.

But a shared context object is still a shared context object. Emphasis on object.


puts is ugly, 0 based arrays should die, and $_ are a bit more then ugly. They actually get passed around between methods, so they can be dangerous. Useful? Some, but there's too many of them to pay attention.
#9/#10. Ruby doesn't believe in bondage and discipline.
 
There are very valid reasons for using zero-based arrays besides historical legacy. This paper by Dijkstra presents it well: Why numbering should start at zero
 
Fascinatingly enough to me is that Matz is talking about:


****REMOVING****

the Perl-style magic stuff like $_. Why, someone might **REMOVE** something from a language! It's possibly a first!
 
People what is so hard about 0 based arrays? They are totally easy to comprehedn, plus they allow such neat tricks as making sure that you never pass the end of the array by writing
array[ index % length_of_array]. If you would have some other indexing base, you would have
array[ index_base + ( (index-index_base) % length_of_array)] which is both harder to write and harder to read.
 
Why do I need either array[ index % length_of_array] or
array[ index_base + ( (index-index_base) % length_of_array)]?

If what you want is a ring-like classs, write a Ring-like class. Aren't both of the above constructions leaky abstractions?
 
zero based arrays are plain bad. I wonder why the people who first started it did not think it was just plain unnatural.
 
"puts [is so C++-] ... lispish ruby ... car.new ... new car ... always *gets* called ..."

So, while we are stamping out those silly ungrammatical C++-isms like "puts", can we also get rid of those silly lispisms of 'car' and 'cdr'?

In the future, I hope you will write "puts [is so C++] ... lispish ruby ... automobile.new ... new automobile ... always input called".


[P.S. That's supposed to be *humor*.]
 
You can override new in a class. The default implementation is pretty much equivalent to

class Object
  def self.new(*args, &block)
    obj = self.allocate
    obj.send :initialize, *args, &block
    obj
  end
end

If you want your Pooled connection constructor to do something else, write:

class PooledConnection
  def new()
    @@free_list.pop! || Connection.new
  end
end

#initialize isn't set in stone, it's just Matz applying the Hollywood Principle.
 
Piers:

You can override new in a class.

Sorry, I was unclear. yes, I know that, and it's one of the things I heart about Ruby. I said:

new Car(...) is broken. The new operator is bad unless you can override it.

I was slamming languages where new is a magic operator that cannot be customized or overridden. In Ruby, new isn't an operator and it can be overridden.

The connection pool example is a specific problem in Java: perhaps I should have been even more explicit about the fact that I was describing a Java problem, not a Ruby problem.
 




<< Home
Reg Braithwaite


Recent Writing
Homoiconic Technical Writing / raganwald.posterous.com

Books
What I‘ve Learned From Failure / Kestrels, Quirky Birds, and Hopeless Egocentricity

Share
rewrite_rails / andand / unfold.rb / string_to_proc.rb / dsl_and_let.rb / comprehension.rb / lazy_lists.rb

Beauty
IS-STRICTLY-EQUIVALENT-TO-A / Spaghetti-Western Coding / Golf is a good program spoiled / Programming conventions as signals / Not all functions should be object methods

The Not So Big Software Design / Writing programs for people to read / Why Why Functional Programming Matters Matters / But Y would I want to do a thing like this?

Work
The single most important thing you must do to improve your programming career / The Naïve Approach to Hiring People / No Disrespect / Take control of your interview / Three tips for getting a job through a recruiter / My favourite interview question

Management
Exception Handling in Software Development / What if powerful languages and idioms only work for small teams? / Bricks / Which theory fits the evidence? / Still failing, still learning / What I’ve learned from failure

Notation
The unary ampersand in Ruby / (1..100).inject(&:+) / The challenge of teaching yourself a programming language / The significance of the meta-circular interpreter / Block-Structured Javascript / Haskell, Ruby and Infinity / Closures and Higher-Order Functions

Opinion
Why Apple is more expensive than Amazon / Why we are the biggest obstacles to our own growth / Is software the documentation of business process mistakes? / We have lost control of the apparatus / What I’ve Learned From Sales I, II, III

Whimsey
The Narcissism of Small Code Differences / Billy Martin’s Technique for Managing his Manager / Three stories about The Tao / Programming Language Stories / Why You Need a Degree to Work For BigCo

History
06/04 / 07/04 / 08/04 / 09/04 / 10/04 / 11/04 / 12/04 / 01/05 / 02/05 / 03/05 / 04/05 / 06/05 / 07/05 / 08/05 / 09/05 / 10/05 / 11/05 / 01/06 / 02/06 / 03/06 / 04/06 / 05/06 / 06/06 / 07/06 / 08/06 / 09/06 / 10/06 / 11/06 / 12/06 / 01/07 / 02/07 / 03/07 / 04/07 / 05/07 / 06/07 / 07/07 / 08/07 / 09/07 / 10/07 / 11/07 / 12/07 / 01/08 / 02/08 / 03/08 / 04/08 / 05/08 / 06/08 / 07/08 /