
A criticism of Ruby (2013) - tosh
http://madhadron.com/posts/2013-02-25-a-criticism-of-ruby.html
======
woodruffw
I disagree substantively with this paragraph:

> Ruby has no well defined grammar. All the Ruby implementations today reuse
> Matz's original parsing code. There are various BNF grammars people have
> written for the language, but they may or may not match the actual
> implementation. Nor does Ruby provide a mechanism to turn code into an
> abstract syntax tree for you as Python and Lisp do. Anyone writing tools
> beyond a unit testing library must solve this problem first, before ever
> doing any real work.

Ruby's grammar _is_ well-defined, it's just _implementation_ defined rather
than _specification_ defined. That alone is a problem, but I think the author
makes a mistake in associating an external specification with the state of
being well-defined[1].

Being able to turn your code into an AST can be useful, but I think the author
overstates its value. Personally, I've never wanted or needed to inspect
Ruby's AST in a practical application. When I'm fiddling around, I use
whitequark's parser[2][3], which has always worked well.

[1]: It's also worth noting that Ruby _does_ have a formal ISO specification
([https://www.iso.org/standard/59579.html](https://www.iso.org/standard/59579.html)),
albeit for 1.9.x syntax (IIRC).

[2]:
[https://github.com/whitequark/parser](https://github.com/whitequark/parser)

[3]: Which, notably, just takes Ruby's `rubyNN.y` and exposes the AST as Ruby
objects.

~~~
mistercow
Linters typically operate on the AST for your code, though, and differences
between your linter's grammar and the real grammar can cause headaches.

More ambitiously, I think there's a huge amount we could be doing with ASTs,
where right now we're mostly kludging along with line-based tools.
Merging/diffing is one example. I've run up against some truly gnarly merge
situations, in particular with indentation based languages, where automatic
merging did what could best be described as "stirring" the code, and manual
merging was like pulling teeth.

It's bizarre to me that in 2017, we're still using tools that act like our
source code files are made of unstructured lines of text, instead of
exploiting the precise structure that the language imposes. I don't know of
any remotely mature tools that try to solve this problem, although I've seen
some more research-like projects. The potential gains seem huge. For example,
imagine renaming a method throughout your project, and having no merge
conflicts with your teammates, even when they change the same lines where that
method is called. (Or at least, imagine having a merge tool that proposes the
correct merge for your approval.)

But if we're going to move toward tools like that, languages without formal
specifications are an obstacle.

~~~
delhanty
>... there's a huge amount we could be doing with ASTs, where right now we're
mostly kludging along with line-based tools. Merging/diffing is one example.
...

Strongly agree - seems a strange situation to me.

The line-based thinking that still dominates the majority *nix has some
advantages but also does seem to impede progress.

It reminds me that I meant to read Rob Pikes "Structural Regular Expressions"
[0]. (ht #pmoriarty - from HN mid-November [1])

Furthermore, even if there is well-defined AST available, it doesn't mean it
will be used. XML based formats for example - e.g. see here: [2]

If we could even semi-automatically merge Microsoft's Office Open XML formats
[3] anywhere using the associated AST that would surely create huge value for
lots of businesses.

[0]
[http://doc.cat-v.org/bell_labs/structural_regexps/se.pdf](http://doc.cat-v.org/bell_labs/structural_regexps/se.pdf)

[1]
[https://news.ycombinator.com/item?id=15714169](https://news.ycombinator.com/item?id=15714169)

[2] [https://joepairman.com/posts/xml-in-git-mercurial-watch-
out-...](https://joepairman.com/posts/xml-in-git-mercurial-watch-out-for-
merges)

[3]
[https://en.wikipedia.org/wiki/Office_Open_XML](https://en.wikipedia.org/wiki/Office_Open_XML)

------
rajangdavis
I read through this article twice and I don't get it. I feel as though I don't
have enough background to understand the arguments presented.

I use Ruby all the time and love it; can someone simplify the main points of
this article? My impression is that what the author wants and what I want from
a programming language are different, but I don't want to write off his
arguments because of my lack of understanding.

~~~
zkomp
I don't get it either. Neither see the supposed (objective) "errors" in the
language design.

Why not have both raise/rescue and throw/catch when the used are different.
And why not have both methods, functions and closures? It is for different
things... Why read docs when you have irb and can pretty much inspect
everything you need.

All the comparisons with java... I hate it, much rather use ruby style OO than
the horrible boilerplate hell that I remember from java.

Problem with ruby is not that it is not java, it is imho sometimes it is too
easy and forgiving - letting you do crazy things like unintentional meta
programming, and not catching errors until runtime. Other problems with ruby
is complexity and the lack of clear spec (lookup what happened to the rubinius
ruby spec project).

~~~
zbentley
Debuggers/inspectors are great, but they absolutely do not serve the same need
as documentation, and any language/community that recommends the former in
place of the latter should be considered suspect as to the quality,
reusability, and extensibility of its creations.

> Why read docs when you have irb and can pretty much inspect everything you
> need?

Because irb is another tool I have to learn how to use if I'm new to a
language.

Even if I already know the tool, I have to learn how to get the inspector to
my code in order to inspect it in irb. That can be quite hard if I want to
inspect an object that gets described and constructed during the runtime of a
big "harness" I don't control. How do I get the inspector to run precisely
where I need it to? In most situations like this, the code I want to inspect
is itself running in a new process, or on a different server, so "just drop a
breakpoint in", while possible, is far from easy advice to follow.

And all that is assuming that the inspector's output, when I get it, will be
at all useful! What if the code I want to inspect is highly dynamic, and has
different object "shapes" at runtime depending on subtle differences in
inputs? That kind of code isn't obscure in ruby land--I've seen common
situations where inspection is at _best_ a waste of time and at worst
_actively deceptive_ in Puppet, ActiveRecord, and various RSpec-ish
frameworks. That's not to say their code is bad; just that it's a really bad
idea to assume that a inspector can double as a documentation authority.

The alternative--textual documentation--is absolutely essential and cannot be
replaced with a debugger/inspector. I already know how to read English (or
doc-language-of-choice).

To read documentation, I don't need to learn a new tool (designed for
prototype testing and debugging; probably _not_ the first concerns on my mind
if I'm at the "check the documentation/API reference" phase of development),
carefully construct a contrived runtime context to reach my code, extrapolate
the output as intended for the runtime back into something human-intelligible,
and then pray that what I found is the _right_ output, or is in any way
representative of what my code is going to actually encounter when it runs.

~~~
rajangdavis
I agree about debuggers although I have not needed to use anything beyond the
"inspect" method on a given object for what I use Ruby for.

From what I can tell, Pry is supposed to be the debugger of choice when
working with Ruby but I think that's in a Rails context. I haven't touched
Rails in about 6 months but never used Pry for the 3 years that I have worked
with Ruby on Rails.

I think the documentation issue exists in any (dynamic) language; however,
from what I can tell, statically typed languages have better support for this.
The majority of libraries that I use with Ruby (Sinatra, Parallel) tend to
have great documentation; however, it can be a mixed bag.

------
sundarurfriend
I haven't yet finished reading the article, but it's a lot more educational
and interesting than I expected. As a casual Ruby user, I expected maybe a
bunch of bullet points about minor annoyances and issues of taste, but this is
a thoughtful, enlightening piece.

------
twelvechairs
For me as a ruby user, the things that I can agree with (and maybe build upon)
as criticisms here are:

\- Ruby's origin as a single implementation has led to a few ongoing issues in
documentation. This has been generally fixed considerably over time. Its lack
of a '100%' AST still bugs me a little (ruby2ruby and ruby_parser only get you
so far). As noted also, its not really built for some tasks like moving a
method from one module to another. These are all really quite a niche issues
however which wont affect 99.999% of programming.

\- There's a few 'gotchas' in learning Ruby's module, namespace, etc. systems
(include/extend, raise-rescue/throw-catch, return from a block). However, most
programmers learn these reasonably quickly and as a language Ruby is probably
simpler in terms of these issues than others.

\- Ruby's TIMTWODI approach has caused a few areas where there are different
libraries doing the same things in different ways (e.g. as noted unit testing
- though RSpec is great) - a more mature language may just 'pick a winner'.
TIMTWODI's got its advantages though.

However, the author also mixes the above with some very oblique views on
language design. Particularly where it goes down to paragraphs like
'Multiplication of like things', 'M+N' and 'Compilation units' etc. - there's
not much discussion on ruby at all other than rather weak criticisms of ruby's
module/namespace systems at the end, as they dont meet the writers
preconceived ideas of how these should work.

The final assertation that these 'errors in language design' mean you
shouldn't use ruby because 'it is not the only language in its niche' is
really ridiculous however. The author doesn't state what the better
alternative is (or even define what ruby's niche is), and I'm sure any one
which is picked would have its own issues - and these would have at the very
least a different set of tradeoffs than those in ruby.

~~~
philwelch
The obvious niche for Ruby is the niche shared by Perl and Python. Having used
Ruby full-time for 4-5 years and Python full-time for the past year, I think
Python is strictly better than Ruby in the particular areas mentioned by the
author.

I can think of a couple major reasons one would prefer Ruby to Python, but
only a couple:

* Ruby has a more flexible syntax, which means you can write "DSL's" in Ruby. Rake and RSpec are generally given as examples. Unlike actual DSL's, though, Ruby just lets you leave out parentheses so you can pretend that the handful of functions you just call a lot in a Rakefile or an RSpec test are keywords. I'm not sold, but some people may prefer this aesthetically, and there's really no way to argue against that.

* Ruby is all-in on object orientation, in a "poor man's Smalltalk" kind of way. There are advantages to this, since you can just.call.methods.on.everything(like: this) or iterate by calling .each, which is simpler than Python's double-underscore protocols. There are surprisingly few disadvantages, because you don't have to actually write in an object-oriented style in Ruby.

I personally don't find these arguments convincing.

------
mattmcknight
This is an annoying document. He goes into long, wrong explanations of
different concepts, and then provides very minimal examples of how Ruby
doesn't work like Java or some other imagined ideal language does in his
conception of the world. A person that has a particular way of programming
often will struggle trying to impose that way on another language.

Documentation: One criticism is that the documentation is somehow incomplete.
I think [https://www.ruby-lang.org/en/documentation/](https://www.ruby-
lang.org/en/documentation/) and [http://ruby-doc.org/](http://ruby-doc.org/)
neatly dissolve that. The other criticisms appear to be things that would
largely apply if one were trying to implement a Ruby interpreter, which is not
the normal task of a Ruby programmer and are irrelevant, but also largely
wrong. Most Ruby developers are not consulting the source code of the
interpreter to determine what functions do on a regular basis.

Interfaces: This is largely a meaningless complaint about the lack of specific
thing called an interface in Ruby, when it is largely not needed as they are a
mechanism to work around strict typing. The long example of streams is
something that is easy to do in Ruby. The examples are then one case where the
OpenSSL library has a different name for a method than the standard library
for sockets, that the rexml SAX2 parser and Nokogiri are slightly different,
and then some odd complaint about streams again. This is a ridiculous
complaint about different libraries doing things differently, not related to
interfaces at all, and Ruby would let you easily work around this situation if
you ever needed to.

Namespaces, compilation units, and modules: The explanation for this is
tortured and simplistic. You can see confusion shining through when he calls a
Ruby file a compilation unit. It just doesn't work that way. Multiple files
can contribute to a Module or a Class. The supposed criticism here is
incoherent and seems to be incomplete. What is he trying to do? Why can't he
do it? But he can do it; he just doesn't like how it is done?

Multiplication of like things: This is basically a weak criticism of TMTOWTDI,
but using "orthogonal" in an incorrect way that makes the whole thing
nonsensical. It complains that Ruby has too many functional constructs, which
is not the typical complaint one sees.

MN vs. M+N and what it did to the language: Nonsense again, suggesting that
one must use multiple inheritance to write a polymorphic max(a,b) function,
and since he doesn't like that there are two ways to include modules in Ruby,
something is bad? No case made at all.

Tooling and reasoning: This is just a disgusting set of lies. I think he's
never heard of JRuby which has been an active project for 16 years. Try
RubyMine if you want a smart IDE for Ruby.

"The criticisms above are not matters of taste. They are errors in language
design." Absolutely incorrect on two counts, they are almost all taste, and
many of them have zero to do with language design. How about this- if they
were errors, that would suggest that there is a right way to X, Y, and Z.
Instead, we are given examples of how language A does something, and a
statement that Ruby does it differently. Given that we are dealing with humans
here, there are clearly large numbers of humans that prefer the Ruby way.
Given that there is nothing listed here that can't easily be done by a single
line of Ruby code, it hardly stands up as evidence of any error in language
design.

Frankly, I find the preferences expressed here bizarre. The attempt to tie
them to language design errors just seems stupid.

~~~
philwelch
> Documentation: One criticism is that the documentation is somehow
> incomplete. I think [https://www.ruby-
> lang.org/en/documentation/](https://www.ruby-lang.org/en/documentation/) and
> [http://ruby-doc.org/](http://ruby-doc.org/) neatly dissolve that.

The language itself is finally documented, but libraries are extremely hit-
and-miss and the lack of features to support extracting documentation from
code is a huge reason for this. Having used both Ruby and Python extensively
(and Ruby for many more years), the difference is pretty staggering.

> The other criticisms appear to be things that would largely apply if one
> were trying to implement a Ruby interpreter, which is not the normal task of
> a Ruby programmer and are irrelevant, but also largely wrong. Most Ruby
> developers are not consulting the source code of the interpreter to
> determine what functions do on a regular basis.

But we do consult the source code of every library and framework we use.

As for the advantages of having a well-defined grammar or the ability to
inspect the AST, those are things I don't think you appreciate the value of
until you have the opportunity to use them. Linting and optional static typing
are two things I've come to appreciate.

To name another example: I once worked on a Python project where we had to
swap out database drivers across every Python codebase in the entire
organization, and, as you point out, different libraries do things differently
so we had to change the code around all our DB calls. One of our senior devs
used astor--a Python library that supports loading Python code and
transforming it at the AST level--to write a script to perform this
transformation programmatically on our code repos. There was still lots of
testing and review of the generated code changes, but it was a lot nicer than
having to do it all by hand.

> Namespaces, compilation units, and modules: The explanation for this is
> tortured and simplistic. You can see confusion shining through when he calls
> a Ruby file a compilation unit. It just doesn't work that way. Multiple
> files can contribute to a Module or a Class.

I think you might have misunderstood the author here, because files are indeed
"compilation units" in Ruby. If I have a running irb session where I've
imported code from 20 different Ruby files, and I change the code in one of
those files, I can just call load on that file again and it should fix
anything up, even if that file is monkeypatching a class that's also defined
in seven other files.

~~~
mattmcknight
"because files are indeed "compilation units" in Ruby. If I have a running irb
session where I've imported code from 20 different Ruby files, and I change
the code in one of those files, I can just call load on that file again and it
should fix anything up, even if that file is monkeypatching a class that's
also defined in seven other files."

I should have been more clear, Ruby is not compiled. It is interpreted.
Compilation units are a concept from the Java Langauge Spec. That concept does
not clearly map to Ruby.

~~~
philwelch
The Ruby interpreter compiles to bytecode, which is then passed to the VM,
which isn’t that far off from classical Java. JRuby also seems to support
ahead-of-time compilation.

In any case, the fact that a single Ruby source file can be incrementally
loaded into the runtime despite altering classes and modules defined elsewhere
supports, rather than detracts, from the author’s notion of a “compilation
unit”. Python, in contrast, tightly couples code files (“compilation units”)
with modules.

------
norswap
I concur. Shameless self plug about something I wrote about the first issue
(documentation): [http://norswap.com/ruby-specification-
problem/](http://norswap.com/ruby-specification-problem/)

------
djr96
"Nor does Ruby provide a mechanism to turn code into an abstract syntax tree
for you as Python and Lisp do"

That's not even correct, ruby's standard library contains a Ripper class that
does just that:

[http://ruby-doc.org/stdlib-2.0.0/libdoc/ripper/rdoc/Ripper.h...](http://ruby-
doc.org/stdlib-2.0.0/libdoc/ripper/rdoc/Ripper.html)

------
flavio81
I found it a well explained article. The author throws good, pointed
references to Smalltalk, Lisp, Python and C in the comparison, and indeed
there are areas in which Ruby seems to be a bit awkward.

However i'd say there is little criticism of Ruby there and more of a
comparison of typical aspects of different programming languages.

------
codinghorror
Has any of this been addressed in recent Ruby releases? The velocity of the
project has improved since 2013.

------
dsjoerg
"They are as hard to confuse in your memory as a small Chinese woman with a
giant black man."

------
stevebmark
This is so good. This person is eloquent and to the point and clearly very
smart.

------
andrevan
"The criticisms above are not matters of taste" Actually, yes they are.

~~~
hacknat
Okay. He spent more time on his argument than you did countering it. Care to
elaborate? It’s not self-evident to me that you’re right.

~~~
DannyBee
To be able to say something is an error in the sense the author uses requires
the ability to determine objective, rather than subjective, correctness. The
author asserts, incorrectly, that there is some objective correctness in
choice of interacting language features and their interactions. No such thing
usually exists. It is all subjective at some level. It is not more or less
wrong to choose one spec for a language feature over another. It is just
taste. The closest you can come to objective correctness or incorrectness is
incorrectness of an implementation in implementing a language specification or
inability of a specification to correctly describe behavior.

Ie given a precise definition of how a language feature should operate, does
it operate that way.

Most criticisms of programming languages, including this one, try to take the
fairly abstract goals of most language features and pretend that they can
show, objectively, that the feature does not meet the goals. The problem, as
mentioned, is that most of these goals are very subjective. So are the
criticisms. They are not saying "Ruby claims to have the following algebraic
specification for modules, but I prove that is impossible" they are saying
"here is my subjective interpretation of the following be goal Ruby has. I can
objectively show it does not meet that goal"

Such a thing is still a subjective criticism, not an objective error.

~~~
philwelch
I can see your point, but, to pick a couple specific points—it is strictly
better to have thorough documentation than not to, and it is also strictly
better to have a language with a defined grammar to support linters and the
like than not to. These aren’t trade-offs, or else, I don’t see anything that
Ruby has that wouldn’t be possible without the drawbacks the author notes.
Perhaps you know of some features or strengths that Ruby has that wouldn’t be
possible without these drawbacks?

~~~
DannyBee
"I can see your point, but, to pick a couple specific points—it is strictly
better to have thorough documentation than not to, and it is also strictly
better to have a language with a defined grammar to support linters and the
like than not to."

These are still subjective choices, not errors. Think of it this way: It is
almost always strictly better to use O(N) sort algorithms instead of N^2 ones.
That does not make it an error to use an N^2 one, just a bad choice.

"These aren’t trade-offs"

Sure they are. They are tradeoffs in the same way N^2 vs N sort algorithms
are. There are edge cases and weird situations where the N^2 ones are better.
There are edge cases and weird situations where less thorough documentation is
better. Are they common? No. I would venture a guess that most people, in
fact, do not believe they are worth worrying about, and would roll their eyes.
(IE imagine you have a super space limited device. Documentation that is not
thorough and only concentrates on the "most important" parts may fit where
thorough docs wouldn't)

Same with defined grammars. Having a well defined grammar means people depend
on it. It gives you less freedom. To be useful to others, it also often
requires being LL or LALR or some variant, which limits how you can extend the
language in the future.

You are entirely welcome to say "all of the situations you present are super
contrived and stupid". I would even agree. They are just examples i put 2
minutes thought into (ruby isn't my thing, i wrote some apps in it years ago
and haven't touched it in probably 5 years). But the point is: They are still
tradeoffs, even if you think they are obviously better or worse choices.

"Perhaps you know of some features or strengths that Ruby has that wouldn’t be
possible without these drawbacks?"

Certainly you realize how subjective a thing this is? Even if the answer is
"nothing would be worse" it doesn't suddenly make it objectively better
instead of subjectively better. Everyone agreeing with an opinion does not
turn it into a fact. Again, i think it would be reasonable to say "for the
majority of ruby users, X would be better" and even "for 99% of ruby users, X
would be better". But that's still subjective.

~~~
philwelch
> Think of it this way: It is almost always strictly better to use O(N) sort
> algorithms instead of N^2 ones. That does not make it an error to use an N^2
> one, just a bad choice.

The distinction between an "error" and a "bad choice" isn't all that
interesting to me, and this approach more or less makes it meaningless to
criticize the design of literally anything.

~~~
DannyBee
"The distinction between an "error" and a "bad choice" isn't all that
interesting to me"

I definitely wouldn't agree personally, but to each their own.

" and this approach more or less makes it meaningless to criticize the design
of literally anything."

I can't fathom why you think this. All i answered is what the difference
between the two types of criticism are. The only problem with this criticism
is that the author is trying to claim these things are objectively wrong,
repeatedly, and they are not. In fact, the author doing that just makes the
author look silly, for starters.

I can't see why that would make it meaningless to criticize the design of
anything. It would, IMHO, be much better than current situation where the
author pretends his criticisms are _not_ subjective, when they very clearly
are.

Remember that objective things are true for all observers. So the author
presenting it this way also means they claim it can't be viewed another way.
That's not exactly an invitation to a discussion about the tradeoffs.

It would be perfectly reasonable for the author to say "I disagree with these
choices for these reason, and i think my choices are better for these
reasons". Awesome! Let's talk about it.

It's like writing an article claiming that choosing random pivots in quicksort
is an error versus choosing median of 3. Or even better, claiming that using
median of 3 is an error compared to median of median of 3. They aren't errors,
and claiming they are is honestly not a very useful criticism.

~~~
philwelch
So, let me provide another example.

Let's say I write a C compiler. The C specification says that certain things
have "undefined behavior", which technically means the program could do
anything. So my C compiler somehow outputs programs where many of these
"undefined behaviors" actually result in reformatting the hard drive.

If you define "error" by "deviating from a written specification", this is not
technically an error, because "undefined behavior" can be anything. Yet, if
someone actually used my underhanded C compiler to compile mission-critical
production software, I feel comfortable characterizing that as an objectively
bad decision--and so would the soon-former employer of that poor fool.

You might say my example is contrived, and so is yours, so where does the
truth lie? I think it goes back to my original point: it is strictly better to
have thorough documentation than not to, and it is also strictly better for a
language to have a defined grammar to support linters and the like than not
to. Semantic quibbling about what is an "error" and what is "objectively
wrong" aside--I think all (reasonable) observers would agree with these two
points, which brings them as close to objective truth as we can get as far as
this question is concerned.

------
Exuma
Yawn. If you don't like it, don't use it. I don't understand peoples obsession
with writing huge thought pieces on why they don't like something.

~~~
danharaj
Why write anything?

~~~
falsedan
To impress potential partners.

