RubyConf TH 2022

Roasting the Duck - A Talk About Ruby and Types

A talk from RubyConfTH, held in Bangkok, Thailand on December 9-10, 2022.

Find out more and register for updates for our 2023 conference at https://rubyconfth.com/

RubyConfTH 2022 videos are presented by Cloud 66. https://cloud66.com

RubyConf TH 2022

00:00:17.359 We almost always focus on how fast things change and how quickly technologies evolve. It's hard to stay on top of things. However, things do not always change in the same direction. There are aspects of computing, and programming in particular, that tend to swing back and forth over the years. If you disagree, it might be because you haven't been in programming for long, as the pendulum can take a long time to return. Eventually, someone in the room will say, 'Hey, this is how we did it 20 years ago!' One thing happening now is a shift back towards static languages after a significant push for dynamic languages around the 2000s. We're seeing a lot of effort to incorporate static type checking into dynamic languages.
00:01:08.700 Many people are excited about this trend, including Ruby, which is often regarded as one of the most dynamic languages. Ruby introduced RBS, a way to add some static typing into Ruby. How many of you are using RBS or at least have tried it? A few hands, not many. That's understandable because RBS can seem somewhat suspicious. It raises the question: Are we turning Ruby into a static language? It’s easy to forget the importance of duck typing in Ruby. So let's talk about types in particular because they are confusing. If you look up type systems on Wikipedia, you'll find many categories and terms. Unfortunately, many of these terms can lead to misunderstandings, such as 'static' versus 'dynamic'. We all use these terms, but the distinction gets hazy.
00:01:50.800 When we talk about static languages and dynamic languages, if I were to be a nitpicker—though I typically am not—I would argue that these terms don’t mean much in a meaningful way. It's not the language itself that is static or dynamic; it's more about the typing. Moreover, if I were even more pedantic—which, again, I'm not—I would say it’s about type checking. What matters is when your language checks the types. So, essentially, every modern language is going to check that the method you're calling accepts a string. The timing of the check is what distinguishes static from dynamic typing.
00:02:30.900 Static type checking occurs before runtime, meaning the language checks your types before executing the code. Usually, this is done by a compiler. Now, this doesn’t imply that you have to declare the types of variables upfront; it just means that someone needs enough information to check the types, typically requiring you to provide type signatures.
00:03:00.300 For example, in C#, you often see type signatures and type declarations clearly outlined. While in some cases, like deducing variable types, the compiler infers that the variable is an integer without explicit declaration. Some languages, such as Crystal, push the idea of type inference further. Crystal is statically typed but allows for type declarations to be inferred. That's static type checking as opposed to dynamic type checking, which occurs at runtime. Dynamic type checking, for instance, is common in Python, where you typically don't need to declare your types. In Python, the interpreter checks the values during execution and raises an error if a type mismatch occurs.
00:04:03.720 So what's the difference? Static type checking often leads to fewer runtime errors and can improve the development experience. It can make your code easier for the computer to understand, which in turn aids in better auto-completion, navigation, and tooling. On the other hand, dynamic type checking allows for greater flexibility—especially in distributed systems where you might be working with diverse data types. It facilitates tolerance to unexpected data, which is crucial in a networked environment. Overall, static and dynamic type checking serve different purposes and contexts in programming.
00:05:24.000 One major difference and a very confusing aspect is the distinction between strong and weak typing. Many programmers mistakenly use these terms interchangeably with static and dynamic. To illustrate this, imagine a hypothetical language that does no type checking whatsoever. In contrast, many modern languages do a degree of type checking, hence being more strongly typed relative to those that do not check at all.
00:06:57.360 The terms strong and weak typing concern how strictly a language enforces its type system once it is established, while static and dynamic refer to when the type checking occurs. It's also worth mentioning that while people use these terms, the confusion surrounding them often results in miscommunication. There has been a significant shift in the past decade towards adding static type systems to languages designed to be dynamic, aiming to combine the benefits of both approaches. Transpiling is one method of implementing static typing in dynamic languages, with TypeScript being a prime example.
00:08:21.840 TypeScript allows for type signatures while still compiling down to JavaScript, making it a remarkable tool for modern development. Another method is to use annotations, where you don't necessarily have a dedicated type checker built into the language, but you can still provide type information in the form of inline annotations within your code. In Ruby, this is facilitated by Sorbet, the most popular type checker for Ruby, which requires you to use annotations to define types in your Ruby code. This method maintains the dynamic nature of Ruby while incorporating static type checking through Sorbet.
00:09:40.320 For instance, if you define a class in Ruby, you can also create a parallel RBS file that outlines the type information without needing to include the actual implementations. This allows for the types to be checked while keeping the dynamic flexibility of Ruby intact. By loading RBS files into IDEs, developers gain enhanced documentation and navigation capabilities, improving their overall workflow within Ruby. The RBS system is a somewhat new experiment for Ruby aimed at merging the advantages of both static type and dynamic type checking.
00:11:09.840 Now, the question is whether such a shift is beneficial or not. Some might argue that it resembles static type systems too closely for languages like Ruby, which are traditionally built around dynamic typing. There is, indeed, an ongoing debate within the programming community about the merits of static versus dynamic type checking. To be candid, I find that this debate lacks depth and often devolves into unconvincing arguments for either side. Proponents of static typing often tout its ability to catch bugs early, whereas advocates for dynamic typing highlight its flexibility.
00:12:40.380 However, it's become increasingly clear that both approaches bring invaluable benefits to programming over the years. In my humble opinion, learning and mastering a mix of both static and dynamic languages can significantly enhance a programmer’s skill set. For example, static typing aids in more complex software development, suggesting that it can foster better tooling and overall development experience. Conversely, dynamic type checking is indispensable when designing systems that are heterogeneous in nature—like those that run across different environments on the internet.
00:14:25.680 Looking toward the future, I encourage you to explore RBS, especially if your projects are large and you find types confusing. Using tools that support RBS can improve clarity and maintainability. Similarly, if you're working on a library that will be consumed by others, providing clear type annotations will enhance user experience. Ultimately, mastering a blend of static and dynamic approaches can be quite advantageous.
00:15:57.600 As a final note, while these concepts may seem contradictory at times, they are not mutually exclusive. We should embrace both static and dynamic type checking as they are suited for different scenarios. The essential takeaway is that each approach fulfills a distinct role within the realm of programming, and it’s crucial as developers to leverage the strengths of both to create robust and maintained software.