Soutaro Matsumoto and the Ruby team over at Square have a proposal for typing in Ruby 3. My initial reaction was a befuddled sort of confusion as though I were a dog who woke up with a human’s hands. It took several readings for me to understand why Ruby developers would ever want this. We’ve all been to Reddit and such places where other development communities poke some nice (and at times not so nice) spirited japes about Ruby scaling for larger projects and some of the difficulties that might come with that. After reading the post several times, I am a little less skeptical than before and can see some cases where this might make sense.
So let’s start with the premise that this is all based on: large Ruby projects can be difficult to maintain due in part to dynamic typing. There’s no point in arguing if that’s a true premise or, even if it is, if this is a problem worth solving. For the purposes of this post, we are accepting that premise as true. To be clear, we’re starting this look at Square’s proposed solution with the assumption that we actually want to solve this problem; I’m putting together another piece with some thoughts on how valid the argument against dynamic typing really is.
Taking a ten-thousand-foot view, this is implemented via adding a new file type, .rbs. These files would contain the type signatures for the associated .rb file and are described as analogous to C++ / Objective-C header files or .d.ts files from Typescript. Admittedly that sounds like a pretty big change to make to the language, but it’s actually intended to minimize change for existing code. While Ruby 3 is going to be a fairly substantial update, pains are being taken to avoid creating a schism between the 2.x line and the 3.x Ruby lines; if you know anything about Python or just know any Python developers, you’ll know that there’s to this day a huge division between Python versions 2 and 3. In that context, the addition of .rbs header files does indeed help to serve that aim, since it shouldn’t require your .rb (ie existing Ruby code) to change or at least you shouldn’t have to change it just to use these enhanced typing features.
Obviously, just by having stronger types (note I don’t say “strong”), it’s true that tooling can more easily find entire classes of logic errors in your code. This is an argument that proponents of strong typing have been making against dynamically typed languages for as long as anyone in the industry can remember, so let’s not litigate that here. Instead, let’s take a look at some of the more interesting benefits.
Rubyists will be familiar with the term duck typing. If you’ve never heard the term it’s a reference to the old “walks like a duck, quacks like a duck…” saying; in other words and grossly simplified, if an object has the methods of a type, we can infer it’s probably that type. In Matsumoto and the company’s proposal duck typing is supported. Indeed, it’s enhanced by the introduction of interfaces to the language. If you’re familiar with Java or most other programming languages, you probably have a good idea of what interfaces in Ruby will be like. Sure enough, an “interface type represents a set of methods independent from concrete classes and modules” as stated in their post. Eagle-eyed coders will note that the implementation here is somewhat different than say from Java, however, the pattern of interfaces having methods that force the implementing class to conform to certain type expectations is very simpler than what you’d in classical Java interface patterns; I am fully aware that Java interfaces do all kinds of fun stuff these days and am only referring to simple cases here. Because, I am a little greedy, my hope is that interfaces can be used for more than just typing safety. Even if that’s not true, I expect to see interfaces to be quickly adopted in larger code-bases; I know I will.
These typing changes add what seems to be the most fashionable language feature of the last year or so, nil safety. Because the type checkers that utilize .rbs will understand optional types, they will be able to do all the magic nil safety checking stuff we see in other languages. This is all fine as far as I am concerned and again, it’s one of those features you can decide to use or not. Speaking of type checkers, the post mentions that if you’re using Sorbet, currently the most popular static type checker for Ruby that is incidentally made by Stripe, is not being targeted for replacement by .rbs and this proposal and should be able to work with it just fine.
Putting my cards on the table, I was skeptical of the proposal at first. However, after giving it some deeper thought and going through it in more detail, I’m on board. The only claim that I find a little dubious is that widespread adoption of .rbs won’t lead to a decline in Sorbet usage. I have no dog in this fight at all; in fact, The Mad Botter is a Stripe customer for our direct sale SAS / app products. Let me know what you think on Twitter and subscribe to my podcast.