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.