3.0 • Not much used yet…😢 Why? • What we need is "a great developer experience" • Type inference is not enough! • Now working on TypeProf v2 • IDE support is a primary goal 3 def foo: (Integer)->String def foo(n) n.to_s end 5.ti| 1 + "str" Is this a bug? Do you mean: 5.times
types, error detection • go to definition, completion, flow-sensitive analysis • type popup by hover (since v2) • inline RBS in a comment (since v2) • The analysis is very responsive! • Currently, <0.1 sec. per edit • I am now actually dogfooding TypeProf v2 • to develop TypeProf v2 itself 7
tool for Ruby • Assumptions • Analysis speed was not important • The target code was complete 11 NoMethodError: String#upcaase def foo(n) "str".upcaase n.to_s end def foo: (Integer) -> String
IDE support, not a type itself • Assumptions • Analysis speed was not important → Very important! • The target code was complete → Incomplete! 12 5.ti| Do you mean: 5.times 1 + "str" Is this a bug?
tool • But it was not enough to improve the developer experience • Adding IDE support later was difficult • The algorithmic assumptions are quite different ➔ Reboot TypeProf v2 with IDE-aware algorithm 13
second for each edit • Target: Medium-sized projects (~10K or 100K LOC?) • Larger projects should be split to gems (or directories) • API between gems should be declared by type definitions • Key approach: Incremental update of the analysis result 15 0.1 second is about the limit for having the user feel that the system is reacting instantaneously J. Nielsen, "Usability Engineering", 1993
code to a dataflow graph • vertex: local variable, etc. / edge: dataflow • Flow types from literals to vertexes 16 y x "str" String .size String#size String z Integer x = "str" y = x z = x.size String
.to_s Integer#to_s Integer Float Integer#to_f .to_f r foo( ) 123 def main x n def foo(n) n.to_f end def main x = 123 r = foo(x) ... end Integer Changed
to a dataflow graph • Flow types to the graph • Incremental update • Replace only a subgraph of a modified method • Q. A subgraph change may lead to long type propagation? 22
time possibly • even if subgraph change is small • But not so often under 10K LOC (hopefully) • At least, I haven't experienced a terrible chain in dogfooding • Almost all methods accepts consistent types • For worse case / For larger projects • Write type declarations to stop propagation 27
analysis of many dependencies • Foo#something, Foo::Something, @something in Foo • Subclass determination on Foo, etc… • Limits superclass changes to at most one per edit • First, only class definitions and constants are analyzed • to fix the class inheritance relationships • Then, other expressions are analyzed 30 class Foo < Bar class Foo < Baz
other than a specific type • e.g., var cannot be nil in a then clause of "if var" 31 s String s NilClass String String NilClass Filter vertex # s: String|NilClass if s # s: String s.gsub(...) end
• Module#include, attr_reader, etc. • TypeProf treats them as syntax, not method calls • Future work: we will need to support DSL individually • has_many, belongs_to, … in Rails • it, let, … in RSpec 32
27 files, 7,000+ LOC • TypeProf v1: • An analysis takes 3 sec. • Incremental update is unsupported (i.e., each edit takes 3 sec.) • TypeProf v2 • A full analysis from scratch: 1.003 sec. • An average incremental update per edit: 0.029 sec. • Note: It could be slower if we proceed with the development 34
based on dataflow graph • SpiderMonkey's type inference algorithm • B. Hackett, et al. "Fast and Precise Hybrid Type Inference for JavaScript", PLDI 2012 36 https://ternjs.net/
RBS syntaxes • Implement and improve IDE features • More error diagnostics • Definition jumps for local variables, instance variables, etc. • Completion for various-type method calls, constants, variables, etc. • Displaying documents, etc. etc. etc. • Dogfooding, dogfooding, dogfooding… 37
the message of NoMethodError • Rationale: #inspect can be very long • Welcome feedback: https://bugs.ruby-lang.org/issues/18285 40 undefined method `firrst' for [1, 2, 3]:Array Ruby 3.2 undefined method `firrst' for an instance of Array Ruby 3.3 (planned)