RubyKaigi 2022 -- Tsu, Mie, Japan September 10, 2022
Soutaro Matsumoto (@soutaro)Ruby programming with typesin actionThis work by RubyKaigi 2022 Team is licensed under a Creative Commons Attribution 4.0 Unported License.#BF4545 #B8A562 #755B8E #4E6994 #41414F #2C2C31 UFYUDPMPS
View Slide
@soutaro• Developer of Steep & RBS• Ruby committer• Working for Square BlockSoutaro Matsumoto
RBS & Steep• We havefive talks related to RBS/Steep this year• Generating RBS is big topic• How can we use the type definitions?• Show people the types instead of callseq• Type checking! 💪
RBS 2.7.0.pre.1Updates 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 sigdef foo: { () [self: String] -> void } -> voiddef instance_eval: [A] { () [self: self] -> A } -> A$ gem install rbs --pre
Type checking benefitsWhen we have a typed Ruby code base• Understanding code• API documentation• Adding a new feature• Refactoring the feature
DemoWhat can we do with typed Ruby code• Hover• Goto definition• Completion• Type errors
Demo
Demo 2Getting started with Steep1. Setup2. Generate RBSfile prototypes3. Run type checker4. Add more types
Demo app• Point-of-sale app• Models• Order• OrderItem• CashPayment• CardPayment
Demo 2
Focus on the part you work on• You don't have tofix all of the type errors when you start• Give types to the related part as you need• Type checking a few smallfiles would be good for exerciseDetected 1759 problems from 85 files
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(...)elserender status: :not_foundend# def sample: () -> Element?unless array.empty?random = array.sample or raiseendDo something else if nil It cannot happen
Help Steep type check your code# @type var out_dir: Pathname?out_dir = nil# @type var pairs: Array[[String, Integer]]pairs = []Declare the variable typesCast to untyped or skip type checking 1 + (_ = "")__skip__ = 1 + ""MoneyData = Struct.new(:currency, :amount) do# @implements MoneyDatadef exchange_to(currency, rate)Support meta-programming
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 efforts, little benefits• Let's write new code with Steep 💪• There must be unknown bugs
Demo 3Adding new feature with types• Defining APIs in RBS• Writing implementation with type checker
Add search feature"caffe latte"
Add search feature"caffe latte" before:2022-08-01 paid-by:cash
Demo 3
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::Basequery.build_relation()# @type var query: Query::Name | Query::Before | Quercase querywhen Query::Name...when Query::Before...when Query::PaidBy...endInheritance Union types
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
Working with types• Types are abstraction of your code• Easier to write/change than implementation/tests• Validates consistencies between components unlike design docs• Type definitions and implementation are iterative• Write types → write code → types → code → ...App code Tests RBSDesign docsWhiteboardDetailed Abstract
Can I do it with RubyMine?• RubyMine has limited support for Steep• But, it provides another type checker integrated with RBS, and you candevelop Ruby programs with types using RubyMine
Recap• More benefit 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