The State of Ruby 3 Typing
Soutaro Matsumoto
@soutaro
Slide 2
Slide 2 text
The State of Ruby 3 Typing
• Ruby 3 will ship with a feature to support type checkers.
• A type de
f
i
nition language RBS and rbs gem.
• RBS type de
f
i
nitions of standard libraries.
• Several type checkers are available for Ruby.
• You choose the best type checker for your projects.
Slide 3
Slide 3 text
Type Checkers for Ruby
Steep (@soutaro)
TypeProf (@mame)
Sorbet (Stripe)
The most widely used type checker for Ruby.
A static type checker for Ruby written in Ruby.
Infers types in Ruby programs.
(Bundled with Ruby 3.)
RDL (Je
f
f
Foster)
Research project from Tufts University.
Slide 4
Slide 4 text
Static Type Checking
conference = Conference.new("RubyConf 2020")
talks = [...]
conference.talks.push(*talks)
conference.each_speaker do |speaker|
puts "%s (%s)" % [speaker.name, speaker.email]
end
Slide 5
Slide 5 text
Static Type Checking
conference = Conference.new("RubyConf 2020")
talks = [...]
conference.talks.push(*talks)
conference.each_speaker do |speaker|
puts "%s (%s)" % [speaker.name, speaker.email]
end
Does new method accept a String argument?
Slide 6
Slide 6 text
Static Type Checking
conference = Conference.new("RubyConf 2020")
talks = [...]
conference.talks.push(*talks)
conference.each_speaker do |speaker|
puts "%s (%s)" % [speaker.name, speaker.email]
end
Does new method accept a String argument?
What is the type of the value of #talks?
Slide 7
Slide 7 text
Static Type Checking
conference = Conference.new("RubyConf 2020")
talks = [...]
conference.talks.push(*talks)
conference.each_speaker do |speaker|
puts "%s (%s)" % [speaker.name, speaker.email]
end
Does new method accept a String argument?
What is the type of the value of #talks?
What is the type of the speaker?
Slide 8
Slide 8 text
What is RBS?
• A language to de
f
i
ne types for Ruby programs.
• Classes, modules, methods, instance variables, mixins, ...
• A library and assets to help type checker developments.
Slide 9
Slide 9 text
# The conference object represents a conference.
#
# conf = Conference.new("RubyConf 2020")
# conf.talks << talk1
# conf.talks << talk2
#
class Conference
# The name of the conference.
attr_reader name: String
# Talks of the conference.
attr_reader talks: Array[Talk]
def initialize: (String name) -> void
# Yields all speakers of the conference.
# Deduped, and no order guaranteed.
def each_speaker: { (Speaker) -> void } -> void
| () -> Enumerator[Speaker, void]
end
Slide 10
Slide 10 text
# The conference object represents a conference.
#
# conf = Conference.new("RubyConf 2020")
# conf.talks << talk1
# conf.talks << talk2
#
class Conference
# The name of the conference.
attr_reader name: String
# Talks of the conference.
attr_reader talks: Array[Talk]
def initialize: (String name) -> void
# Yields all speakers of the conference.
# Deduped, and no order guaranteed.
def each_speaker: { (Speaker) -> void } -> void
| () -> Enumerator[Speaker, void]
end
conference = Conference.new("RubyConf 2020")
talks = [...]
conference.talks.push(*talks)
conference.each_speaker do |speaker|
puts "%s (%s)" % [speaker.name, speaker.email]
end
Slide 11
Slide 11 text
# The conference object represents a conference.
#
# conf = Conference.new("RubyConf 2020")
# conf.talks << talk1
# conf.talks << talk2
#
class Conference
# The name of the conference.
attr_reader name: String
# Talks of the conference.
attr_reader talks: Array[Talk]
def initialize: (String name) -> void
# Yields all speakers of the conference.
# Deduped, and no order guaranteed.
def each_speaker: { (Speaker) -> void } -> void
| () -> Enumerator[Speaker, void]
end
conference = Conference.new("RubyConf 2020")
talks = [...]
conference.talks.push(*talks)
conference.each_speaker do |speaker|
puts "%s (%s)" % [speaker.name, speaker.email]
end
Slide 12
Slide 12 text
# The conference object represents a conference.
#
# conf = Conference.new("RubyConf 2020")
# conf.talks << talk1
# conf.talks << talk2
#
class Conference
# The name of the conference.
attr_reader name: String
# Talks of the conference.
attr_reader talks: Array[Talk]
def initialize: (String name) -> void
# Yields all speakers of the conference.
# Deduped, and no order guaranteed.
def each_speaker: { (Speaker) -> void } -> void
| () -> Enumerator[Speaker, void]
end
conference = Conference.new("RubyConf 2020")
talks = [...]
conference.talks.push(*talks)
conference.each_speaker do |speaker|
puts "%s (%s)" % [speaker.name, speaker.email]
end
Slide 13
Slide 13 text
# The conference object represents a conference.
#
# conf = Conference.new("RubyConf 2020")
# conf.talks << talk1
# conf.talks << talk2
#
class Conference
# The name of the conference.
attr_reader name: String
# Talks of the conference.
attr_reader talks: Array[Talk]
def initialize: (String name) -> void
# Yields all speakers of the conference.
# Deduped, and no order guaranteed.
def each_speaker: { (Speaker) -> void } -> void
| () -> Enumerator[Speaker, void]
end
conference = Conference.new("RubyConf 2020")
talks = [...]
conference.talks.push(*talks)
conference.each_speaker do |speaker|
puts "%s (%s)" % [speaker.name, speaker.email]
end
Slide 14
Slide 14 text
RBS Features
Union types
Generics
Interface types untyped type
String | Symbol
Array[String | Integer]
Integer? #== Integer | nil
Array[Integer]
Hash[String, Array[Integer]]
def eval: (String) -> untyped
interface _IntegerConvertible
def to_int: () -> Integer
end
Slide 15
Slide 15 text
https://github.com/ruby/rbs
Slide 16
Slide 16 text
RBS for Gems
• The main targets of RBS are Stdlib types and Gem types.
Written by Used by
Stdlib types Ruby committers (community) All Ruby developers
Gem types Gem authors, community Gem users
App types The developer of the app The developer of the app
Slide 17
Slide 17 text
Ruby
Gem types in RBS
Stdlib types in RBS
App types App types App types
Type checker A Type checker B Type checker C
Slide 18
Slide 18 text
Ruby
Gem types in RBS
Stdlib types in RBS
App types App types App types in RBS
Type checker A Type checker B Type checker C
Lib types
Slide 19
Slide 19 text
Type Checkers and RBS
Steep
TypeProf
Sorbet
Uses RBI as it's native type declaration language.
Will support RBS too. (Maybe only a subset.)
Uses RBS as the primary type language.
Reads library types from RBS and generates RBS.
Slide 20
Slide 20 text
FAQ
• Why it requires writing types in other
f
i
les?
Slide 21
Slide 21 text
Why in Different Files?
• To avoid writing types in Ruby code.
• Matz believes type annotations will be outdated in future and wants no
types written in Ruby code.
• To keep type checking optional in Ruby.
• To support libraries written in C.
Slide 22
Slide 22 text
Inline Type Annotations
• Types in Ruby code is out of scope of RBS.
• We know type checkers need inline type annotations.
class Conference
extend T::Sig
sig do
params(name: String).void
end
def initialize(name:)
@name = name
@speakers = T.let([], T::Array[Speaker])
class Conference
# Method types are in RBS in Steep.
def each_speaker(&block)
# @type var speakers: Array[Speaker]
speakers = []
talks.each do |talk|
speakers << talk.speaker
end
Sorbet Steep
Slide 23
Slide 23 text
RBS of Gems
• Type checkers need type de
f
i
nitions of libraries == We need RBS of gems!
• Distributing RBS
• Releasing gems with RBS
• RBS of existing gems
• Writing RBS
Slide 24
Slide 24 text
Releasing Gems with RBS
• Add sig directory and put RBS
f
i
les there.
Slide 25
Slide 25 text
RBS of Existing Gems
• Write RBS for gems and publish it in https://github.com/ruby/gem_rbs
• Save RBS
f
i
les in gems/gem_name/version directory
Slide 26
Slide 26 text
RBS of Existing Gems
• Multiple versions supported
• Copy the version directories to make new version
• Optimistic version resolution
• Incompatible with semantic versioning
• Required version 3.2.1 will resolve to 4.2 if there is no better version
available
Slide 27
Slide 27 text
Writing RBS of Gems
• You can generate RBS from Ruby code
• Sord will support generating RBS from YARD docs
$ rbs prototype rb lib/**/*.rb
$ rbs prototype runtime -r goodcheck
$ typeprof exe/goodcheck
The State of Ruby 3 Typing
• Ruby 3 will ship with a feature to support type checkers.
• A type de
f
i
nition language RBS and rbs gem.
• RBS type de
f
i
nitions of standard libraries.
• Several type checkers are available for Ruby.
• You choose the best type checker for your projects.
• https://github.com/ruby/gem_rbs