Slide 1

Slide 1 text

Soutaro Matsumoto (@soutaro) Ruby programming with types in action This work by RubyKaigi 2022 Team is licensed under a Creative Commons Attribution 4.0 Unported License. #BF4545 #B8A562 #755B8E #4E6994 #41414F #2C2C31 UFYUDPMPS

Slide 2

Slide 2 text

@soutaro • Developer of Steep & RBS • Ruby committer • Working for Square Block Soutaro Matsumoto

Slide 3

Slide 3 text

RBS & Steep • We have fi ve talks related to RBS/Steep this year • Generating RBS is big topic • How can we use the type de fi nitions? • Show people the types instead of callseq • Type checking! 💪

Slide 4

Slide 4 text

RBS 2.7.0.pre.1 Updates since Ruby 3.1 (RBS 2.0) • Type of self in blocks/procs (Steep 1.2 will support) • Batch prototype (RBS 2.5) • $ rbs prototype rb --out-dir=sig/app app • RDoc plugin (Supported by Google Summer of Code) • $ rdoc sig def foo: { () [self: String] -> void } -> void def instance_eval: [A] { () [self: self] -> A } -> A $ gem install rbs --pre

Slide 5

Slide 5 text

Type checking benefits When we have a typed Ruby code base • Understanding code • API documentation • Adding a new feature • Refactoring the feature

Slide 6

Slide 6 text

Demo What can we do with typed Ruby code • Hover • Goto de fi nition • Completion • Type errors

Slide 7

Slide 7 text

Demo

Slide 8

Slide 8 text

Demo 2 Getting started with Steep 1. Setup 2. Generate RBS fi le prototypes 3. Run type checker 4. Add more types

Slide 9

Slide 9 text

Demo app • Point-of-sale app • Models • Order • OrderItem • CashPayment • CardPayment

Slide 10

Slide 10 text

Demo app • Point-of-sale app • Models • Order • OrderItem • CashPayment • CardPayment

Slide 11

Slide 11 text

Demo app • Point-of-sale app • Models • Order • OrderItem • CashPayment • CardPayment

Slide 12

Slide 12 text

Demo 2

Slide 13

Slide 13 text

Focus on the part you work on • You don't have to fi x all of the type errors when you start • Give types to the related part as you need • Type checking a few small fi les would be good for exercise Detected 1759 problems from 85 files

Slide 14

Slide 14 text

Focus on the part you work on • You don't have to fi x all of the type errors when you start • Give types to the related part as you need • Type checking a few small fi les would be good for exercise Detected 1759 problems from 85 files

Slide 15

Slide 15 text

Make your code safe against nil • The only one way to handle nil safely: test every time before using it • Optional type helps you # @type var order: Order? if order = Order.find_by(id: params[:id]) order.update(...) else render status: :not_found end # def sample: () -> Element? unless array.empty? random = array.sample or raise end Do something else if nil It cannot happen

Slide 16

Slide 16 text

Help Steep type check your code # @type var out_dir: Pathname? out_dir = nil # @type var pairs: Array[[String, Integer]] pairs = [] Declare the variable types Cast to untyped or skip type checking 1 + (_ = "") __skip__ = 1 + "" MoneyData = Struct.new(:currency, :amount) do # @implements MoneyData def exchange_to(currency, rate) Support meta-programming

Slide 17

Slide 17 text

Writing new code • Type checking existing code is 😫 • You know there is no critical bug there • Steep will detect minor problems that forces you add workarounds • Many e ff orts, little bene fi ts • Let's write new code with Steep 💪 • There must be unknown bugs

Slide 18

Slide 18 text

Demo 3 Adding new feature with types • De fi ning APIs in RBS • Writing implementation with type checker

Slide 19

Slide 19 text

Add search feature "ca ff e latte"

Slide 20

Slide 20 text

Add search feature "ca ff e latte" before:2022-08-01 paid-by:cash

Slide 21

Slide 21 text

Demo 3

Slide 22

Slide 22 text

Inheritance vs union types • Choose one that is more suitable for the problem • Steep helps you keep case-when safe and up to date # @type var query: Query::Base query.build_relation() # @type var query: Query::Name | Query::Before | Quer case query when Query::Name ... when Query::Before ... when Query::PaidBy ... end Inheritance Union types

Slide 23

Slide 23 text

Inheritance vs union types • Inheritance 㱻 method call, union types 㱻 case-when • Known set of data types → union types + case-when
 Unknown set of data types → inheritance + method call • Expression problem • Union types are generalization of optional types • Integer? == Integer | nil • Union types help you enumerate all cases

Slide 24

Slide 24 text

Working with types • Types are abstraction of your code • Easier to write/change than implementation/tests • Validates consistencies between components unlike design docs • Type de fi nitions and implementation are iterative • Write types → write code → types → code → ... App code Tests RBS Design docs Whiteboard Detailed Abstract

Slide 25

Slide 25 text

Working with types • Types are abstraction of your code • Easier to write/change than implementation/tests • Validates consistencies between components unlike design docs • Type de fi nitions and implementation are iterative • Write types → write code → types → code → ... App code Tests RBS Design docs Whiteboard Detailed Abstract

Slide 26

Slide 26 text

Can I do it with RubyMine? • RubyMine has limited support for Steep • But, it provides another type checker integrated with RBS, and you can develop Ruby programs with types using RubyMine

Slide 27

Slide 27 text

Recap • More bene fi t from types when you write new code • Writing types and implementation • Anti-patterns without types can be a best practice • Easier trial and error with types than with implementation