That useless Ruby syntax sugar that emerged in new versions
...and what might be interesting about it
Status update: since my last blog post, I’ve finished my training and spent four months close to battlefront (last couple of months near Robotyne), trying to do my best there. Now, I am still in AFU but have recently moved to a more computer-related position, so I have some time to write in Ruby and about Ruby. And so I do.
Ever since meeting it some 20 years ago, I love Ruby.
Somebody might say that nobody should feel emotional about their engineering tools, but I don’t know a law prohibiting the appreciation of means of expression. The means that make me think in new ways, solve problems with joy, and invent concepts that haven’t existed previously.
During my years with Ruby, I (mostly) enjoyed observing how the language changes. And I am proud to be a small part of those changes.
I believe—and have expressed this belief many times—that this process is natural and inevitable for any means of expression. As the world changes, problems change, and our understanding of what and how we want to express changes, too. What once was esoteric or academic becomes an everyday task. The complicated ideas that needed a wordy explanation before become ubiquitous enough to be just briefly referenced.
I think I coined the term “Ruby Evolution”—or, at least, promoted it as something natural and positive. I have written about it in numerous blog posts (1, 2, 3, 4), focused on it in my RubyConf talk, and maintain a site documenting this evolution.
In recent years, there have been quite a few additions to the language’s syntax and standard library.
Frequently, there was a vocal push-back in the community, ranging from honestly subjective (“I don’t like it,” “looks ugly to me”), through pretending-to-be-objective (“it hurts readability”), to something that looks like a measurable statement, only one that nobody knows how to measure (“it gives very little gain to significant drawbacks.”) In several cases, the push-back went as far defining linter rules to prohibit a new feature completely.
Most of the time (though not all of the time), discussing such feedback is futile: too frequently, it boils down to personal tastes and feelings, and, well— As my family proverb goes, “the crocodile doesn’t eat herring. Why? Because he doesn’t want to!”
What I feel as an interesting mind exercise, though, is providing a thought framework of looking at the new syntaxes and language features to understand what are their causes, reasoning, and consequences.
In other words, I want to propose to the reader my analysis of some of the most prominent (and, consequently, the most controversial) ones through the latest versions.
But first, a bit of generic considerations.
How I think about the (programming) language
So, my approach to coding (I tend to call it “lucid code” since recently) states that my language should allow me to:
state the intention/meaning of the code as clearly as possible, so the brief reading would already give a good overview of what’s going on;
is easy to overview and follow, so the reading would be a predictable experience;
avoid stating the obvious, so the reader won’t need to focus on unnecessary things, trying to understand whether they add some meaning or just here because the tool requires it to be written;
use a discoverable dictionary, so it would be easy for the reader to investigate where the particular element is coming from and how it is related to other elements;
if possible, be insightful, making something “click” for the reader in understanding the system the code is related to and making it easy to imagine what else can be done here.
Note that all of those values are highly informal, high-level, and humane.
I am not talking about DRY or “removing all the boilerplate”—but rather about “not stating the obvious” (some things traditionally considered the “boilerplate” are actually adding non-obvious context for the reader; some repetitions are valuable for clarity).
I am not also talking about code “as short as possible”—and vice versa; some redundancy in statements and some spacing in formatting is frequently a quality of the lucid code. That’s how we, humans, read and write.
So, it is all about the comfort of the reader—but the comprehension-related comfort, allowing one to grasp large parts of the system quickly. That’s why I avoid the term “readability,” which has many conflicting usages but is frequently concerned about the ease of reading each particular line by itself—sometimes, even emphasizing that it should be “easily readable for a novice.”
For the record, “should be readable for a novice” is not a totally objectionable statement. Still, it might become when treated narrowly as “shouldn’t contain any element other language/older versions of Ruby don’t have.” Which is just disrespectful for novices: being a novice mentor for many years, I can assure you that motivated junior programmers find joy in understanding new constructs when they make life easier!
The tremendous help to the reader and the writer is provided by following the language intuitions (another important phrase for me… I’ll write a book called “Ruby Intuitions” once the war is over). I believe a well-designed, consistent language imposes a lot of implicit intuitive knowledge about “how things are typically done, what this combination of elements might mean, what this particular name is related to,” and alike. Acquiring those intuitions creates fluency in reading and writing the code that follows them.
For an attentive programmer, those intuitions might run ahead of reality: “in other places of the language, this is possible; oh, usually you don’t make me repeat this much; this looks like a valid and meaningful code.” That’s where the new features frequently emerge!
So, new language features ideally follow existing language intuitions of “what should be possible/easy to write,” pushing them further and producing new intuitions on the way.
Back to the details
Over the next couple of weeks, I’ll publish small(ish! it is always the case with my writing) posts about some of the features that have caused controversy and fierce discussions recently. The structure of each post would be the same:
why it was requested;
how it was designed and why;
how other languages solve the problem (if applicable);
what are the consequences for Ruby intuitions of the new syntax, and how does it make one think in the code; what are the quirks/problems that emerged with the feature.
The features I plan to cover:
Numbered block arguments (Ruby 2.7);
Pattern matching (introduced in Ruby 2.7, took shape over a few next versions);
Hash/keyword arguments values omission (Ruby 3.1)
“Endless” methods (3.0)
Feel free to ping me with suggestions about other features that Ruby has gained over the course of its evolution if you want them covered!
Thank you for reading. Please support Ukraine with your donations and lobbying for military and humanitarian help. Here, you’ll find a comprehensive information source and many links to state and private funds accepting donations.
If you don’t have time to process it all, donating to Come Back Alive foundation is always a good choice.
If you’ve found the post (or some of my previous work) useful, I have a Buy Me A Coffee account now. Till the end of the war, 100% of payments to it (if any) would be spent on my or my brothers’ necessary equipment or sent to one of the funds above.