Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby programming with types in action

Ruby programming with types in action

RubyKaigi 2022 -- Tsu, Mie, Japan
September 10, 2022

Soutaro Matsumoto

September 10, 2022
Tweet

More Decks by Soutaro Matsumoto

Other Decks in Programming

Transcript

  1. 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
  2. @soutaro • Developer of Steep & RBS • Ruby committer

    • Working for Square Block Soutaro Matsumoto
  3. 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! 💪
  4. 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
  5. Type checking benefits When we have a typed Ruby code

    base • Understanding code • API documentation • Adding a new feature • Refactoring the feature
  6. Demo What can we do with typed Ruby code •

    Hover • Goto de fi nition • Completion • Type errors
  7. Demo

  8. Demo 2 Getting started with Steep 1. Setup 2. Generate

    RBS fi le prototypes 3. Run type checker 4. Add more types
  9. Demo app • Point-of-sale app • Models • Order •

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

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

    OrderItem • CashPayment • CardPayment
  12. Demo 2

  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. Demo 3 Adding new feature with types • De fi

    ning APIs in RBS • Writing implementation with type checker
  19. Add search feature "ca ff e latte"

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

  21. Demo 3

  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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