
Ruby mistakes - caludio
https://gist.github.com/c8188956cd66c38feb1f
======
rauljara
While there are a couple of things in there that annoy me about Ruby, the list
basically feels like a stereotypical American pointing at a stereotypical
Frenchman and saying, "Look at that French guy. He looks funny."

Yes, different languages do things differently. I like discussions about the
tradeoffs. I get a lot out of discussions like that.

But these WTF sort of lists are pretty worthless. The implicit argument is
that OMG this language is broken. Yet for any major language, people have been
able to build some pretty complicated stuff with it. Clearly all these WTF
things are at the very least surmountable. And a lot of them stop seeming so
WTF if you actually make some effort to think about or learn why they're
there.

~~~
norswap
Ruby is not a "broken" langage. I use it and like it.

But yes those are WTF things, and you have to know what they are. I remember
that upon encountering it I couldn't wrap my head around throw/catch and
raise/rescue. "Surely it can't that bad" I told myself. It was.

The same goes for the so-called "module system". Spitting on java is very hip,
but its module system is way better than Ruby is.

And to the other commenter, yes those are mistakes. Calling them mistakes does
not imply that the langage is beyond hope or unusable.

~~~
halostatue
I have absolutely no clue what you're talking about WRT throw/catch and
raise/rescue being confusing and bad. It's not. They do different things and
have different purposes. I can't think of any time that I have used
throw/catch, but I absolutely know its purpose, and its parallel nature to
exceptions make frameworks like Sinatra fairly elegant.

Ruby also doesn't have a so-called "module system", so I have no clue what you
or the OP are talking about.

Both classes and modules in Ruby can be used for namespacing purposes. Modules
tend to fill the default "pure namespace" role, but that isn't their
purpose—it just happens to be convention. The purpose of a module is an
interface (per Java) with implementation—a mixin.

If, however, you're referring to the way that ruby programs are structured and
how Kernel#require works…you're still barking up the wrong tree.

Yes, Ruby has mistakes—these two items that you've mentioned are not. Neither
are most of the things that the OP's gist mentions. Some things on the list
are confusing (break/next/return semantics; Yehuda Katz has a good article
highlighting it that I found after a discussion with Reg Braithwaite
[raganwald] and others on Twitter that had started the night before at a bar);
most of them are simply the author's ignorance—and I don't mean that
disparagingly. I am ignorant of many things about Python and JavaScript,
because they are not my primary programming languages.

Ignorance can be easily cured. Arrogance is much harder to cure.

~~~
norswap
By "module system", I mean a way to handle namespacing. I checked a few facts
out, and it's actually less bad than I thought. The main problem I see is that
it doesn't warn you of collisions when including modules. But if you catch a
collision yourself, you can resolve it.

About throw/catch and raise/rescue. First, why have throw/catch and not have
it do what it does in every single other langage? I guess this is debatable.

Secondly, a problem with throw/catch is that there is no "clean" way to know
if you exited via a throw or normally (the hackish way involves a boolean
variable). I guess you could argue that it isn't the intended use. I'm missing
this because of the following point.

My real grief with raise/rescue is that it acts exactly like Java's
throw/catch, while it could be so much more. Why couldn't we throw arbitrary
objects? And pass arbitrary objects to rescue, which would have a special
method (lets call it "catches?(e)") that would check if the objects "matches"
a thing that was thrown. Exception classes would have this as a class method,
and it would match its own instances.

This, incidentally, is exactly how the ruby case statement works. In that
case, the special method is "===". So that's an inconsistency in the langage
right there I'd say. Or at the very least, a missed opportunity.

So ok, maybe not "mistakes" if that's too strong of a word for you. But
"things that should change"? Definitely.

Also, ad hominems are lame.

~~~
halostatue
1\. You shouldn't be including modules without knowing what you're including.

2\. throw/catch isn't an exception handling system. You do have a clean way to
know whether you exited via throw or normally; see
[http://rubylearning.com/blog/2011/07/12/throw-catch-raise-
re...](http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue-im-so-
confused/) for details (it's a nice little summary). The problem is that you
seem to think that throw/catch is for exception handling because everyone else
uses those terms for exceptions.

My understanding is that throw/catch (while used less often) is cheaper to set
up than begin/raise/rescue/ensure/end.

3\. raise/rescue _does_ behave like Java's throw/catch in that it's explicitly
for exception handling. As far as throwing arbitrary objects, let me share a
beauty from C++:

    
    
        try
        {
            throw 0;
        }
        catch (...) // The only thing that catches 0.
        {
        }
    

C++ doesn't restrict what objects can be thrown. It's a horrendous disaster
for the language. Exceptions—including stack unwinding, etc.—are hideously
expensive (and they're worse in Java with checked/unchecked exceptions) and
should be used only for the worst possible things.

You _want_ raise/rescue to work like throw/catch. If you want that, use
throw/catch and save raise/rescue for real exception handling. Or even ignore
both of them.

I think that what you've described are neither mistakes, inconsistencies,
missed opportunities, or things that should change.

4\. I didn't use an argument from ad hominem. I carefully deconstructed the
argument on technical merits and noted that these sorts of arguments come from
ignorance—of which I have in spades on many topics. I also noted that it is
harder to swallow the arrogance that deems arguments from ignorance as
"mistakes". Both of these statements are factual.

True story: like many people who discovered Ruby after having used a number of
different languages, I had substantive amounts of ignorance about why the
language was what it was. In 2002—having _just_ picked up the language, I
suggested that nil should act like NULL in SQL (e.g., only tested through
special case handling). This was ignorance. It was also arrogance to suggest
this for a language that at the time I had been using for less than a month.

[http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-
talk/...](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/42410)

I thought I argued the point very well and looking at the follow-ups, it
wasn't as bad as I remembered, but the proposal wasn't very…Rubyish. (Going
back through to find a few other examples, I found this gem
<[http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-
talk/...](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/43705>);
and I can think of several cases, including the original PDF::Writer, where I
had to do just that.)

It _is_ harder to correct a problem of arrogance than a problem of ignorance.
The person who wrote the original list was both ignorant (new to Ruby, to be
expected, and explicitly Not A Problem) and arrogant (labelling them as
mistakes with Ruby as opposed to decisions/behaviours that weren't
understood). There's a lot that I don't understand about Python, but I am (at
this point, past 40) no longer so arrogant that I declare the explicit self or
the way that len(x) is implemented to be mistakes in the design of the
language (even though I think they are warts that make Python much less
pleasant for me to use).

------
pilif
_> Encoding system is beyond broken_

I disagree. Due to some political issues[1], in Ruby's country of origin,
Unicode is still not as widely used as it could be. Instead, local multibyte
character sets are the norm.

Normalizing to Unicode internally (like what Python 3 does) is impractical or
not feasible as turning that Unicode data back into the original string is not
reliably possible but might be required.

Ignoring encodings all together and treating everything as byte strings (like
1.8.7 did) might be ok if you live in an english speaking country and you're
not dealing with EBDIC. In all other cases, this means that you can't safely
use any of the string manipulation built into the language.

As such the second best way to deal with encodings is to have a distinct type
for every possible encoding you support and have them incompatible with each
other.

Then the applications can decide what they want to do: Not deal with encodings
at all (if you live in pure-ASCII land), unify everything to UTF-8 (or 16), or
use whatever local character set the input data is in.

The only thing I would be willing to argue might have been a wrong decision is
adding the force_encoding method as more often than not calling that is _not_
what you want, even though it might look as if it was. It has a huge shooting-
your-own-foot potential.

OTOH, sometimes libraries have mistakes or lie willingly at which point it
might be a handy, albeit really dangerous, tool to have. Just like the -f flag
for rm. Use it responsibly.

1) <http://en.wikipedia.org/wiki/Han_unification>

~~~
Xylakant
call it "cultural" instead of "political" and you're on the mark.

force_encoding! is sometimes needed if you get data from inputs that pretend
to be a different encoding than they actually are. It does offer a lot of rope
to hang yourself with though.

~~~
halostatue
It's a bit of both. It's cultural insensitivity at the time it was done that
resulted in the better part of a decade where Unicode was stalled in the East.

------
xentronium
> You can change the encoding of a string. Just jesus christ wow.

erm, wat?

Edit:

To refute some of the points:

> _Why on earth does defined? return a string?_

Why not? Strings are perfectly valid in boolean context.

> _Using blocks for looping and callbacks_

again, wat?

> _break/next/return semantics in blocks extremely bizzare_

This is a somewhat valid point. Well, don't do it then, if you don't know what
you're doing!

> _And they have different behavior in procs vs. lambdas_

> _Mutable objects have a hash method and can go in Hashes. Why!!!_

Why not? It is possible to shoot yourself in the leg, but otherwise a very
useful feature.

> _Special case of flip-flop and regexp in an if statement (only if it
> appears_

> _syntactically though!)_

> _Setting `$=` to something truthy causes all string operations to become_

> _case-insensitive. This and other magic globals from perl are mind blowing._

Perlisms.

> _`f {}`. Tell me what the parsing of that is._

Calling f with empty block.

> _Ruby's module system makes namespacing optional (and off by default)._

So?

> _Regexp with named matches decompose into local variables. Dear lord why._

No they don't necessarily, no.

    
    
        1.9.3-p327 :006 > "abcdefg".match(/(?<x>abc)/)
        #<MatchData "abc" x:"abc">
        1.9.3-p327 :007 > x
        NameError: undefined local variable or method `x' for main:Object
    

> _Encoding system is beyond broken_

wat?

> _Scopes: constants, class vars, instance vars, methods, locals. wtf._

Valid point.

> _Constants aren't constant. Truth in naming._

So?

> _Thread locals are really fiber locals._

Valid point.

Overall, a very weak rant.

~~~
masklinn
> Why not? Strings are perfectly valid in boolean context.

That's not a good reason for anything, by that yardstick Array#length could
return a float and nil#nil? could return an array. And worse, the `?` postfix
in Ruby normally means the method returns an actual boolean, not a "boolean
context" (convention over configuration only works if the convention is
respected). You can't XOR a string and a boolean (no `^` defined on string,
although XOR-ing a boolean and a string will work), you _can_ XOR two
booleans.

> Why not? It is possible to shoot yourself in the leg, but otherwise a very
> useful feature.

I'm not sure, but I think he phrased it badly and asks why "interpreter-
provided" mutables (e.g. Array) can be set as hash key: while "user objects"
are also hashable by default in Python, built-in collections and the like are
specified as unhashable as the semantics are not really sensible:

    
    
        >>> {{}: 1}
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        TypeError: unhashable type: 'dict'
        >>> {set(): 1}
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        TypeError: unhashable type: 'set'
    

versus

    
    
        irb(main):002:0> {[] => 1}
        => {[]=>1}
        irb(main):003:0> {{} => 1}
        => {{}=>1}

~~~
myndpage
>And worse, the `?` postfix in Ruby normally means the method returns an
actual boolean, not a "boolean context"

That's not true. Predicates methods returns Truthy or Falsey values. Not
necessarily booleans. See [http://blog.leshill.org/blog/2012/03/25/a-question-
of-truth....](http://blog.leshill.org/blog/2012/03/25/a-question-of-
truth.html)

~~~
nirvdrum
To be fair, this was one side of that argument. A lot of people adopted the
boolean convention, not the truthy one. And the few examples like "defined?"
and "nonzero?" really seem more like quirks than conventions. In five years of
doing Ruby, those examples built into the language and the Rails shitstorm are
the only places I've seen this truthy convention adopted.

Original intent aside, if your language is prided on convention, then it has
to be open to the convention changing.

~~~
Xylakant
One of the quotes in the linked blog post is the pretty much official
statement of the languages creator. defined? and nonzero? are core language
methods and unlikely to change. The fact that many ruby programmers have a
misconception about what predicates were supposed to imply is sad, but won't
change things. In short: All ruby devs need to learn how predicates were
intended or they're in for a surprise.

Btw: The mantra "convention over configuration" is a rails mantra and not a
ruby mantra.

~~~
nirvdrum
I'm expecting core methods to change either, as that would undoubtedly break
programs. Incidentally, this would be kinda funny because the argument about
your program being written poorly if it fails with such a change would be
turned on its head. But, in any event, to pretend that Ruby was a perfectly
designed language and couldn't possibly have warts is weird.

Also, I never said "convention over configuration," since there's nothing to
configure here. I was talking specifically about the convention of what a "?"
should return. In that same quote Matz also says that predicates typically
return a boolean value, but it's not required. That seems to both imply and
endorse a convention.

~~~
Xylakant
> That seems to both imply and endorse a convention.

Sure. The convention endorsed is "should, but not required". That's exactly
what happens. A pattern I see often is something along the lines of

    
    
       def ssl?(url)
         url.match /^https/
       end
    

which returns nil in the case the url doesn't start with https and a matchdata
object if it does.

    
    
       jruby-1.6.8 :092 >      def ssl?(url)
       jruby-1.6.8 :093?>        url.match /^https/
       jruby-1.6.8 :094?>      end
        => nil 
       jruby-1.6.8 :095 > ssl? "https://google.com"
        => #<MatchData "https"> 
       jruby-1.6.8 :096 > ssl? "http://google.com"
        => nil

~~~
nirvdrum
I think we may be in more agreement than either is letting on. However, in
most arguments on this matter, the "should" part seems to just get ignored.

Now in that whole Rails hoopla, it turned into "it's not required and neither
defined? nor nonzero? do it," ignoring the whole "should" part. And now people
are pointing at Rails as another example, reinforcing their own bias.

~~~
Xylakant
I agree with you that it might be nice if you could actually rely on it, but
OTOH I have never personally encountered an error caused by a non-boolean
predicate.

However, it's not only nonzero? or defined? that don't return boolean values.
see <http://news.ycombinator.com/item?id=5074676> for more examples. If you
read through the core libs documentation you'll find more examples. You just
cannot rely on predicates returning true/false in all cases, so you either
have to learn not to rely on it at all or learn every example where it
doesn't. So just don't rely on it.

------
nsmartt
I don't have an issue with most of these. In fact, to me, "ruby allows you to
do X, and it shouldn't" is an annoying line of thought. If I want to make my
language dance, I don't want it to cry-- I want it to sing along.

There's one that I'll agree with, however, and that's "Inline rescue, no
ability to specify what exception."

------
judofyr
Short version: "Ruby isn't Python!! wat?!?"

~~~
dasil003
Not "wat?!?" but "why!!!". It's not a question, it's an exclamation of pure
shock so intense that incredulity can not even register.

------
pcwalton
Using blocks for looping (i.e. using callbacks) is actually a great feature.
See Oleg Kiselyov's spirited defense of callbacks (as opposed to iterator
objects) here: <http://okmij.org/ftp/papers/LL3-collections-enumerators.txt>

The fact that `break`, `next`, and `return` work the way they do in blocks is
a remarkable simplification; it turns a set of constructs that work only on
loops into functional constructs that work on functions, broadening their
usefulness.

------
masklinn

        Using blocks for looping and callbacks
          * break/next/return semantics in blocks extremely bizzare
    

Actually they're pretty sensible (return is anyway, break/next are a bit weird
but no more so than the rest), the main issues with blocks is the — mentioned
— Proc-v-lambda dichotomy and the — not mentioned — block-not-being-first-
class issues. Although it's hinted at with further mentions of magical
behaviors surrounding blocks.

Blocks, in and of themselves, are a very good base for implementing flow
control.

~~~
seliopou
The break/next/return behavior is not sensible at all. Anybody that comes to
ruby with knowledge of other languages with first-class functions will be
confused by it, in particular return.

Having said that, once you learn the difference it's _not that big a deal_.

~~~
masklinn
> The break/next/return behavior is not sensible at all.

They are. Especially return.

> Anybody that comes to ruby with knowledge of other languages with first-
> class functions will be confused by it, in particular return.

Not necessarily, and especially not if they happen to understand what "block"
means, and why Ruby uses "block" not "lambdas" for its core.

Or if they have the _slightest inkling of knowledge_ of Smalltalk and/or Self.

And either way, unless they've already fossilized the difference is easy to
learn (and solves important issues in imperative languages)

~~~
seliopou
A return statement in a block is more or less a call to an escape
continuation. So is break. Most programmers don't know what an escape
continuation is, though once explained can use them. Even if they did, those
escape continuations are captured implicitly. There's no reason to believe
that people are just going to divine their meaning. And if they're new to
Ruby, they're not going to understand what a block is, pretty much by
definition. It's a feature that's pretty unique to Ruby.

~~~
masklinn
> A return statement in a block is more or less a call to an escape
> continuation.

So?

> Most programmers don't know what an escape continuation is

Not that they have any reason to care.

> There's no reason to believe that people are just going to divine their
> meaning.

I don't think I ever said that.

> And if they're new to Ruby, they're not going to understand what a block is,
> pretty much by definition. It's a feature that's pretty unique to Ruby.

It's not for any known value of "unique". Blocks are present in most C-like
languages, and first-class blocks were introduced by Smalltalk.

------
jaimebuelta
My favourite weird Ruby thing (is on the list, anyway) is that CONSTANTS can
be changed, but it raises a warning... Of course, you can also set up a
mutable CONSTANT (a hash, for example) and change it without a warning.

So, my point is, why are they called CONSTANTS?

~~~
nathan_long
For the same reason private methods are called private, even though you can
still access them with `send`. Ruby does not allow one programmer to handcuff
another; it only allows you to put up warning signs. "I don't expect you to
redefine this reference, and you shouldn't depend on the stability of this
internal method."

~~~
martinced
_"Ruby does not allow one programmer to handcuff another..."_

That's not a valid argument.

 _"Q: Why does your OS allow any program to write anywhere in memory?"_

 _"A: Because we believe in freedom. Our profound belief is that the OS should
not allow to handcuff programmers. They must be free to do anything. They
should be able to write anywhere in memory and anywhere to disk"_

How do you want people to be able to be able to reproduce state and to reason
about a program if f*cking CONSTANTS can be modified!?

~~~
xutopia
Reducto ad absurdum! We're not talking about the same thing with full access
to anyone's memory here. We're talking about boundaries that if you have
control over your software should be handled the way you want anyway.

The type of flexibility that Ruby allows make it a really interesting
language. In 99.999% of cases people never use all these features and it's
considered bad form to do so.

Ruby is one of those languages that depends a lot on cultural queues to work
so well but unlike other languages it doesn't enforce everything on you. It's
a tradeoff and it is by no means better than other languages, just different
and pleases certain developers more than other languages.

~~~
draegtun
Well said. And _s/Ruby/Perl/_ and this same statement still holds true.

------
danenania
My ideal language would include: the intuitive-expressive power of ruby, the
"explicit is better than implicit" mantra of python, the functional structure
of clojure, the malleability and interactivity of lisp, and the unobtrusive
static typing of go.

------
edu
Well, he prefers Python (<https://github.com/alex>). Perfect, I'll keep making
my rubies shinier :)

~~~
masklinn
> Well, he prefers Python

More to the point, he _comes from_ Python.

He's also — I believe — working on a ruby (ish?) implementation (or something
along those lines), which probably colors his view of what is or isn't
annoying. Most of what he lists makes the life of an implementor a living
hell.

------
mnarayan01
Ruby makes a number of tradeoffs in favor of "expressibility" at the expense
of readability (without a _high_ degree of ruby proficiency) and ability to
reason about programs. You can disagree with these tradeoffs, but they're not
mistakes. It also has some things which might be designed differently if the
language started from scratch (e.g. string encoding), but I would still say
its a stretch to call these mistakes.

Ruby also has a number of things which make it "hard" (some would say
impossible) to create fully equivalent alternative implementations. This is
suboptimal, but it is what it is; at this point the level of change required
to make it amiable to alternative implementation would be...prohibitive.

I do agree, however, that the inline rescue with no ability to limit to
anything other than StandardError was a mistake...I've just seen way too many
cases where _far_ more than reasonable got caught. That said, I'm not sure
there would be any great way to add matching on the inline rescue to the
grammar -- I think it would have to be removed.

------
jheriko
I don't get it... I thought ruby was supposed to be a mess like this? its one
of its greatest strengths...

------
gbog
I was in the hopes to find a deeper analysis on the problems encountered by
ruby and rails recently.

We get very interesting and obligatory postmortem analysis for every minute a
popular site is down, but when a major framework is found to have holes,
nobody tells us what could have been done to avoid them, what flaws in the
tools our the team's allowed them, and here comes again the elephant in the
room.

------
paulodeon
My first thought: That's a short list!

------
decklin
If you enjoyed this, I recommend the Unix-Haters Handbook:

<http://en.wikipedia.org/wiki/The_Unix-Haters_Handbook>

(I love Unix, and C, and Ruby, and yes, they're all awful, festering examples
of "worse is better".)

------
ritchiea
This list would be a lot more helpful with some description of why he thinks
these are mistakes.

------
lampe
a rant without a solution to the rants... sry but why this post is voted up so
high? do we wane start a ruby vs python war? both languages are good in there
own ways. I don't even understand how someone can compare theme...

"Look this Car is better then your Pineapple"

~~~
batiste
Na, Ruby and Python are quite close to each other and quite comparable. Which
other language would you compare it to?

~~~
randomdata
Python and Ruby have somewhat comparable syntaxes, but Objective-C and Ruby
are much more comparable in overall language design. If you feel the need to
compare similar languages, it may be a better choice as a starting point. Or
Smalltalk, but I suspect it wouldn't resonate quite as well, as much fewer
people are familiar with it.

------
atomical
I would be interested in seeing something on common Rails mistakes.

------
jongold
— Original poster should have basic social skills but DOESN'T. Worst poster
ever.

------
martinced
_"Mutable objects have a hash method and can go in Hashes. Why!"_

This is indeed terrible. It is broken beyond repair and it's probably because
they thought Java doing something similar with their hashcode() method was
"smart".

It is not that mutability is "evil". It is that contracts provided by the
language / APIs on top of mutability definitely is evil, because it's
impossible to obey / respect the contracts.

It is probably one of the biggest source of hard-to-find / hard-to-reproduce /
hard-to-debug bugs.

The problem goes way further than that in Java that said: the very presence of
"hashcode()" and "equals()" at the very top of the hierarchy (in the Object
class) is fundamentally broken. Even if you _are_ using immutable objects in
Java, you are still deeply f _cked if your classes are non-final. Even if you
classes_ are* only allowing to create immutable objects and _are_ final, you
are still deeply f _cked if you use composition.

Because it is _impossible* to extend a class (or use composition) and satisfy
the hashcode/equals contract.

This was explained in "Effective Java" back in the days.

Now it may even be possible that this very equals/hashcode/mutability SNAFU
is, unconsciously for a lot people, at the heart of the current backlash and
hatred towards mutability.

The fact that it is so broken (and provably broken: the contracts _CANNOT_ ,
as in RFC2119, be respected) may be the reason while functional programming is
making a comeback.

It certainly is for me and I'm not looking back.

------
darrencauthon
"Extremely complex grammar, makes barrier to entry for implementation much
higher"

Compared to... what?

~~~
regularfry
The average, or even more-complex-than-average, alternative language. He's
right, the grammar is a complete pig.

~~~
darrencauthon
Example? I don't understand what is meant by this.

~~~
batiste
Python grammar <http://docs.python.org/2/reference/grammar.html>

Ruby grammar is hard to find but there it is
[http://www.ipa.go.jp/osc/english/ruby/Ruby_final_draft_enu_2...](http://www.ipa.go.jp/osc/english/ruby/Ruby_final_draft_enu_20100825.pdf)

~~~
ch0wn
One of my favorite things about Python 3 is that the grammar actually got
smaller: <http://docs.python.org/3/reference/grammar.html>

It's only three lines, but still.

