RubyConf 2020
The State of Ruby 3 Typing

The State of Ruby 3 Typing

by Soutaro Matsumoto

The video titled "The State of Ruby 3 Typing" features a presentation by Soutaro Matsumoto, a lead Ruby engineer at Square, discussing the new typing features in Ruby 3. Ultimately, Ruby 3 will include a static type definition language known as RBS and support various type checkers. The following key points are highlighted:

  • Introduction to Ruby 3 Typing: Soutaro explains the developments regarding typing in Ruby 3 and emphasizes that while Ruby 3 will not ship with a built-in static type checker, it will introduce RBS and the RBS gem to aid type checking.
  • Static Type Checkers: Several type checkers are available for Ruby, including Soutaro's own Steep and the widely-used Sorbet. These require users to manually annotate types, whereas TypeProf infers types automatically by analyzing Ruby programs.
  • RBS Overview: RBS is introduced as a new language that defines types for Ruby programs. It allows the specification of classes, modules, methods, and their relationships, enabling static type checkers to work effectively.
  • Type Definitions: An example is provided, demonstrating how RBS can clarify method signatures and variable types in Ruby, leading to better understanding and error detection without runtime execution.
  • Flexibility of RBS: RBS allows for customization through features like generics, union types, and optional types, facilitating flexible type definitions that improve code quality.
  • Integration into Libraries: Developers are encouraged to follow certain guidelines when releasing Ruby gems with RBS files. Two methods are recommended: including RBS files alongside their gem or utilizing community repositories.
  • Conclusion: The presentation concludes that Ruby 3 will significantly enhance its typing capabilities with RBS and various type checkers, encouraging developers to adopt them for better type safety and code maintainability. Soutaro invites feedback and questions regarding RBS and Ruby 3 typing.

In summary, the introduction of RBS and supporting type checkers positions Ruby 3 to better serve developers with enhanced static type checking capabilities, leading to cleaner, more reliable code.

00:00:07.279 Well, here we are. We are in the final throws of day number one of the conference. We've only got two primary slots left, including this keynote that will close the day out. I don't know about you, but 2020 has proven to be as fast at this conference as it has the entire rest of the year. So, here we are.
00:00:32.880 Before we proceed, I want to remind you that there is a keynote just following this session. Please make sure to tune in for that. Also, if you haven't been paying attention or noticed some of the chats and announcements happening in the open chat, please check those out because there are a few different things coming up that may be very valuable to you.
00:01:06.880 Without further ado, I want to introduce Soutaro Matsumoto. He is going to be giving us the state of Ruby 3 typing. So, here we go. The stage is yours.
00:01:18.880 Okay, hello, good afternoon. My name is Soutaro. I'm an engineer working for Square in Tokyo. I'm speaking from Japan, and it is currently six A.M. So yeah, it’s early morning, but it's fine.
00:01:31.280 I developed a type checker for Ruby called Steep, and I'm also a member of the Ruby committee team, working on types. Today, I will talk about the state of Ruby 3 typing — what will be happening with Ruby 3 regarding typing.
00:02:12.400 This is a very short summary: Ruby 3 will ship with features to support type checking, including a type definition language called RBS and a library called the RBS gem. It will also include type definitions in RBS for standard libraries, including built-ins like strings and arrays.
00:02:40.160 There will be several type checkers available for Ruby, but we don't have a standard type checker for Ruby 3 yet. You can choose the best type checker for your project, install it, and then you will have a type checking experience with the one you choose.
00:03:08.400 I want to introduce my type checker, Steep. It is a static type checker for Ruby and is implemented in Ruby itself, which means if you want to change something, you can write Ruby to modify the behavior of Steep. Another one I should introduce is Sorbet, which is the first production-ready static type checker for Ruby and is currently the most widely used.
00:03:38.400 These type checkers — Steep and Sorbet — assume that the user writes some type declarations for the classes, modules, methods, instance variables, and constants of their Ruby programs. However, TypeProf is different. It is a type checker developed by the team at Ruby, and it analyzes Ruby programs to infer types without requiring type declarations or annotations.
00:04:05.280 TypeProf works on ordinary dynamically typed Ruby programs. It finds relationships between classes and how methods are called with arguments, and it tries to generate type definitions based on its analysis. This will be bundled with Ruby 3, so you can start using TypeProf immediately after installation.
00:04:37.360 Another tool worth mentioning is RDL, a research project led by Professor Jeff Foster, which has published some research papers about type checking for Ruby using that tool. So, as you can see, we now have at least four type checkers available for Ruby.
00:05:22.080 Now, I would like to explain the idea of static type checking through an example Ruby program. This program makes a Conference class object with two arguments and pushes some of the talks to the ‘talks’ attribute of the Conference instance.
00:05:54.079 The first question we might have is about the type of the 'new' method. We pass a string argument to it, but we don’t know the method signature. It could accept a symbol, an integer, or a keyword argument.
00:06:08.400 Next, for the talks, it looks like it could be an array or something else. If we assume that 'talks' is an array, we can use the 'push' method; however, if it’s nil or something else, we cannot use 'push'.
00:06:31.919 We also make assumptions about the 'each_speaker' method. Since its name starts with 'each', we guess that it accepts a block. However, we need to know what kind of value will be yielded to the block.
00:06:59.680 What is the type of the local variable for speaker? These are the questions we need to consider. If we execute the Ruby program, it will identify incompatible method arguments, but what we want from static type checking is to reason about these issues without running the program.
00:07:26.640 For static type checking, we introduce a feature called RBS. RBS is a new language to define types for Ruby programs, describing the API of Ruby programs, including classes, modules, methods, instance variables, and the relationships between them.
00:08:00.000 With RBS, we can define the structure of Ruby programs. For instance, here’s a simple example of an RBS class definition for a Conference class. This definition shows it has two attributes: name and talks, where the type for 'name' is a string, and 'talks' is defined as an array.
00:08:40.800 The 'initialize' method accepts a string argument, and methods have overloads where if you pass a block, it will yield a speaker object. Using this class definition allows static type checkers to reason about Ruby programs more precisely.
00:09:09.600 For instance, when calling the ‘new’ method, we know it accepts a string argument, confirming the call is allowed.
00:09:23.600 We also know the ‘talks’ method is an array, so we can apply the ‘push’ method, which is valid. When we pass a block to the ‘each_speaker’ method, it will yield a Speaker instance.
00:09:49.760 With RBS, we can detect issues such as passing incompatible objects or calling undefined methods. This is the essence of type checking.
00:10:11.920 RBS incorporates several features, such as generics. The syntax allows defining types like 'Array<Integer>' to specify that it is an instance of Array where the elements are integers.
00:10:50.080 Union types allow flexibility, letting us define a type as 'String | Symbol', which can help avoid rigid class hierarchies.
00:11:18.560 Optional types provide shorthand for union types, indicating that a variable can either be an integer or nil.
00:11:44.799 Interface types support duck typing, letting us specify required methods without assuming any specific inheritance relationships.
00:12:05.680 The underscore prefix in interface types is intentional. These do not appear in Ruby programs directly, avoiding conflicts with class or module names.
00:12:34.400 Lastly, untyped types skip type checking altogether, which is useful for return values that can be highly dynamic, such as those from the eval method.
00:12:53.760 Regarding the RBS library, it contains RBS files for standard libraries and the Ruby standard library itself. While not very extensive, it provides a foundation for type annotations within Ruby code.
00:13:24.320 Next, we can group RBS files into three categories: standard library types, gem types, and application types. Standard library types are for core libraries like String or Array.
00:13:47.200 Gem types come from the RBS files shipped with gems, while application types are defined by developers within their applications. The primary focus for RBS is on ensuring standard library and gem types are robust.
00:14:12.080 We hope that various type checkers will support RBS syntax for both gem and standard library types. However, we are not involving application type definitions at this stage.
00:14:41.440 This is the practical aspect of RBS and how it integrates into various typing tools. For instance, Steep primarily uses RBS for type definitions, whereas TypeProf infers types without requiring user annotations.
00:15:05.920 Sorbet uses its type definition language but plans on supporting a subset of RBS.
00:15:28.960 Let's discuss why RBS requires writing types in separate files rather than embedding them in Ruby code. One reason is to avoid cluttering Ruby files with type information and to keep the language flexible.
00:16:03.680 We aim to keep Ruby feeling optional regarding type checking. Many Ruby developers may not be interested in it, so RBS maintains that flexibility.
00:16:29.120 It's also a practical matter for supporting libraries written in C, where types need to be declared since there is no Ruby code specific to those libraries.
00:16:58.080 When it comes to Ruby gems, the recommendation is to follow the standard library's approach when providing RBS files. Gem developers can offer RBS files, and there are methods for integrating these into their libraries.
00:17:36.960 The first option is to release your gem alongside RBS files. Simply create a ‘sig’ directory and place your RBS files there. When RubyGems processes the library, it will look for this RBS directory.
00:18:17.440 Alternatively, if a gem author does not want to publish their own RBS files, a community resource repository exists for creating RBS files for many popular Ruby gems. This allows developers to benefit from type definitions even when authors don’t provide them.
00:18:50.080 We should support multiple versions of libraries in these repositories. If we want to maintain compatibility across versions, we can organize RBS files into directories named after the versions they support.
00:19:26.640 Semantic versioning applies here, where we can resolve version requirements more flexibly, promoting a better situation with richer type signatures rather than offering none.
00:20:05.760 However, if inconsistencies arise, it’s also feasible to disable RBS loading for specific gems and revert to manual type definitions to resolve any issues.
00:20:40.720 Now, let’s summarize the details. Ruby 3 will indeed ship with features to facilitate objective static type checking, and various static type checkers are coming alongside it. You can select the best solution for your projects.
00:21:10.720 We have set up a GitHub repository, ruby/rbs, that will serve as a collection of third-party RBS files for various libraries.
00:21:42.600 Thank you for watching my presentation. The RBS system has many issues and challenges that we are still addressing, so please let me know if you have any feedback or questions. Thank you again!
00:22:02.720 Oh, okay.
00:22:03.760 Uh.