39 comments

[ 3.0 ms ] story [ 107 ms ] thread
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.
I learned a lot by reading this article, and found it rather interesting. I don't understand people's obsession with writing dismissive comments about HN articles they don't want to read.
if they had to use it for one reason or another the criticism would be justified, eventually.
I think that's short sighted. It worth it to criticise anythong, even the language of the day. So long as those criticisms are instructive.
Has any of this been addressed in recent Ruby releases? The velocity of the project has improved since 2013.
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.
"The criticisms above are not matters of taste" Actually, yes they are.
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.
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.

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?
"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.

> 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.

"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.

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.

do you always defer to the longer argument? OC wasn't trying to convince you, he was just raising an objection.
(comment deleted)
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.

I have a similar set of unpublished notes, ramblings and draft articles on similar subjects and endlessly delay to push them out. My personal conclusion is that Ruby is an appealing language but numerous problematic areas pervasive to the core, the code, and the community can (not automatically) add real, damaging weight to a project.

Those notes aren't just rants, as I'm quite the constructive person and been set to research them and address some, here's an example: https://github.com/lloeki/pak/blob/master/README.md

Unfortunately there is no chance most of the issues will ever see any fix, which makes me wary to use or recommend Ruby for new projects (but I do use it still, only cautiously so and well aware of its limitations)

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).

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.

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.

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), albeit for 1.9.x syntax (IIRC).

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

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

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.

>... 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

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

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

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

For a grammar (or anything else) to be worse than implementation-defined, it would have to be outright flaky: not parse the same way twice based on uninitialized bits in RAM, phase of moon, etc.
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/ and 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.

I learned significantly more reading this comment than I did the article. It's concise, informative and well written. Wonderful work and thank you for sharing your perspective.
> Documentation: One criticism is that the documentation is somehow incomplete. I think https://www.ruby-lang.org/en/documentation/ and 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.

"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.

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.

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.

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.

"They are as hard to confuse in your memory as a small Chinese woman with a giant black man."
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.

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