Slide 1

Slide 1 text

An introduction to typed Ruby programming Soutaro Matsumoto @soutaro

Slide 2

Slide 2 text

Soutaro Matsumoto

Slide 3

Slide 3 text

Soutaro Matsumoto From Tokyo, Japan

Slide 4

Slide 4 text

Soutaro Matsumoto From Tokyo, Japan

Slide 5

Slide 5 text

Soutaro Matsumoto From Tokyo, Japan Working as CTO of Sider

Slide 6

Slide 6 text

Soutaro Matsumoto From Tokyo, Japan Working as CTO of Sider

Slide 7

Slide 7 text

Soutaro Matsumoto From Tokyo, Japan Working as CTO of Sider

Slide 8

Slide 8 text

Soutaro Matsumoto From Tokyo, Japan Develops a type checker for Ruby Working as CTO of Sider

Slide 9

Slide 9 text

Outline • The plan for Ruby3 ← • An introduction to Steep, my static type checker for Ruby • The progress

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

The team From Ruby team Type checker developers Matz Koichi Sasada Yusuke Endoh Dmitry Petrashko and Sorbet team (Sorbet) Jeff Foster (RDL) Yusuke Endoh (type-profiler) Soutaro Matsumoto (Steep) Have meetings to share the progress and develop the foundation of type checking for Ruby

Slide 12

Slide 12 text

The goal • Detecting types of each Ruby expression statically • To help development of Ruby programs • Finding bugs (NoMethodErrors) • Reading code: jump to definition • Refactoring: renaming constants, methods, ... • Performance is not our goal (it needs much more precise analysis)

Slide 13

Slide 13 text

Ruby 3 will have four key items Sorbet Steep RDL Library code type signature Type error warnings Application code type signature type signature Type Profiler Type error warnings 3. Type signature profiling 2. Level-1 checking 4. Level-2 checking 1. Type signature format

Slide 14

Slide 14 text

The plan (rephrased) • Ruby3 will provide: • A language to define signatures of Ruby programs • Signature of standard library • A protocol to share the signatures of gems

Slide 15

Slide 15 text

The Ruby signature language • Ruby-like but different syntax • Defines the structure of Ruby programs • Classes, modules, interfaces, and mixin • Methods and instance variables • Generics, unions, tuples, optionals, ... • Covers most of the standard libraries class Array[A] include Enumerable def []=: (Integer, A) -> A def []: (Integer) -> A? def each: { (A) -> void } -> self def partition: { (A) -> bool } -> [Array[A], Array[A]] ... end WIP

Slide 16

Slide 16 text

Stdlib and gem signatures • To type check your application, you need the signatures of libraries • Standard library and gems • You don't want to write yourself • Ruby3 will ship with standard library signatures • Ruby3 will define the standard protocol to share signature of gems

Slide 17

Slide 17 text

The plan (rephrased) • Ruby3 won't provide: • New syntax to annotate Ruby code • The standard type checker

Slide 18

Slide 18 text

No syntax for type annotation def inject(start): [A] (A) { (A, X) -> A } -> A ... end New type annotation syntax (rejected in Ruby3)

Slide 19

Slide 19 text

No syntax for type annotation def inject(start): [A] (A) { (A, X) -> A } -> A ... end New type annotation syntax (rejected in Ruby3) # @type method inject: [A] (A) { (A, Elm) -> A } -> A def inject(start) end Type annotation using comment (Steep)

Slide 20

Slide 20 text

No syntax for type annotation def inject(start): [A] (A) { (A, X) -> A } -> A ... end New type annotation syntax (rejected in Ruby3) # @type method inject: [A] (A) { (A, Elm) -> A } -> A def inject(start) end Type annotation using comment (Steep) sig { type_parameters(:U). .params(start: T.type_parameter(:U), 
 blk: T.proc.params(arg0: Elem, T.type_parameter(:A)).returns(T.type_parameter(:U))) .returns(T.type_parameter(:A)) } def inject(start, &blk) end Type annotation using embedded DSL (Sorbet)

Slide 21

Slide 21 text

No type checkers • We develop type checkers, but the tools are not a part of Ruby3 • No tool works as Matz expected... • Steep requires too much annotations • Sorbet's modeling of duck typing requires extra code • type-profiler is still too experimental

Slide 22

Slide 22 text

The plan (rephrased) • Ruby3 will provides: • A language to define signatures of Ruby programs • Signature of standard library • A protocol to share the signatures of gems • Ruby3 won't provide: • New syntax to annotate Ruby code • The standard type checker

Slide 23

Slide 23 text

Typing experience in Ruby3 • Choose the best level for your project • My recommendation is level 2 Library Application Type checking Tools Level 0 No No No --- Level 1 Use library signatures No signature Weaker type-profiler Level 2 Use library signatures You write signature Stronger Sorbet, Steep

Slide 24

Slide 24 text

Outline • The plan for Ruby3 • An introduction to Steep, my static type checker for Ruby ← • The progress

Slide 25

Slide 25 text

Steep key ideas • Structural subtyping (for duck typing) • Metaprogramming agnostic • Static type checker without runtime checks/library $ gem install steep https://github.com/soutaro/steep

Slide 26

Slide 26 text

class Conference attr_reader :name attr_reader :talks def initialize(name:) @name = name @talks = [] end def speakers talks.each(&:speaker) end end conference = Conference.new("Balkan Ruby 2019") conference.talks << Talk.new(...)

Slide 27

Slide 27 text

class Conference attr_reader name: String attr_reader talks: Array def initialize: (name: String) -> void def speakers: -> Array end class Talk ... end class Speaker ... end class Conference attr_reader :name attr_reader :talks def initialize(name:) @name = name @talks = [] end def speakers talks.each(&:speaker) end end conference = Conference.new("BalkanRuby 2019") conference.talks << Talk.new(...)

Slide 28

Slide 28 text

class Conference attr_reader :name attr_reader :talks def initialize(name:) @name = name @talks = [] end def speakers talks.each(&:speaker) end end conference = Conference.new("Balkan Ruby 2019") conference.talks << Talk.new(...) class Conference attr_reader name: String attr_reader talks: Array def initialize: (name: String) -> void def speakers: -> Array end class Talk ... end class Speaker ... end ArgumentTypeMismatch: receiver=::Conference.class constructor, expected={ :name => ::String }, actual=::String

Slide 29

Slide 29 text

class Conference attr_reader :name attr_reader :talks def initialize(name:) @name = name @talks = [] end def speakers talks.each(&:speaker) end end conference = Conference.new("Balkan Ruby 2019") conference.talks << Talk.new(...) class Conference attr_reader name: String attr_reader talks: Array def initialize: (name: String) -> void def speakers: -> Array end class Talk ... end class Speaker ... end MethodBodyTypeMismatch: method=speakers, expected=::Array<::Speaker>, actual=::Array<::Talk>

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

rubber duck

Slide 33

Slide 33 text

Duck typing support interface _DumpTo def <<: (String) -> any end class Conference ... def dump_titles: (_DumpTo) -> void end # conference.dump_titles("") # conference.dump_titles([]) # Error conference.dump_titles(3)

Slide 34

Slide 34 text

Metaprogramming agnostic • Steep does nothing about metaprogramming • It detects type errors on metaprogramming method calls • It doesn't know the result of metaprogramming • You can use any metaprogramming as you like class Person # Error: attr_reader doesn't accept class attr_reader Name end class Class def attr_reader: (*Symbol|String) -> ... end class Person @name: Name def name: -> Name end

Slide 35

Slide 35 text

No runtime library • Inline type annotations of Steep are comments • Embedded DSL requires extra runtime dependency • Better for libraries: • Keep # of runtime dependencies as small as possible • Your library users would not want to install Steep spec.add_development_dependency "steep"

Slide 36

Slide 36 text

Outline • The plan for Ruby3 • An introduction to Steep, my static type checker for Ruby • The progress ←

Slide 37

Slide 37 text

The progress • Type checkers • Development in progress • Type definition language • Develop a ruby library to process type definitions • https://github.com/ruby/ruby-signature • Library types • Writing signature of standard libraries • Starting a discussion for gem types

Slide 38

Slide 38 text

My proposal for gem sigs (1) • Ship your gems with signature files • Include signature files in the .gem • Declare in metadata that the gem has signatures Gem::Specification.new do |spec| spec.name = "rakia" spec.files = ["lib/rakia.rb", "sig/rakia.rbi"] spec.metadata = { "signature_dir" => "sig" } end

Slide 39

Slide 39 text

My proposal for gem sigs (2) • Let community write gem signatures for the case the authors don't provide signatures (DefinitelyTyped in TypeScript) source "https://rubygems.org" gem "rails", version: "~> 6.0.0" gem "type--rails"

Slide 40

Slide 40 text

Get ready for types! • Give us feedbacks • Test type checkers with your programs • $ gem install steep • Sign up Sorbet beta program • Try writing types for your gems/apps • Send a patch to add standard library signatures • https://github.com/ruby/ruby-signature

Slide 41

Slide 41 text

References • Projects • https://github.com/soutaro/steep • https://sorbet.org • https://github.com/plum-umd/rdl • https://github.com/mame/ruby-type-profiler • https://github.com/ruby/ruby-signature • Sider • https://sider.review • https://blog.sideci.com/interview-with-bozhidar-batsov-99b049b6fd6a