Try Out Ruby 3.0 Type Checking with Steep
Merry Christmas! Today Ruby 3.0 was released today with type checking. Here’s how to try it out yourself. First, read about Ruby 3.0 type checking here in The State of Ruby 3 Typing.
Install Ruby 3.0 with rbenv
To follow along, you’ll need Ruby 3.0. I use rbenv
. To see the new version 3.0, I updated ruby-version
using home brew.
brew upgrade ruby-build
Next, I was able to install the new version using rbenv
.
rbenv install 3.0.0
After that, I created a new directory to experiment in.
mkdir PizzaFactory
cd PizzaFactory
I created a .ruby-version file to make sure rbenv
was running Ruby 3.0.
echo "3.0.0" > .ruby-version
Double-check you are running Ruby 3.0
ruby -v
You should see the following print out:
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin20]
Define a Pizza class
I created a pizza.rb
ruby file.
class Pizza
attr_accessor :toppings
def initialize(toppings)
@toppings = toppings
end
def to_s
toppings.join(", ")
end
end
Annotate the Pizza class with types using an RBS file
Next, I created a sig/pizza.rbs
file to describe the types I’ll be using.
class Pizza
attr_accessor toppings: Array[String]
def initialize: (Array[String]) -> void
def to_s: () -> String
end
Check the types with Steep
Install steep
to do type checking. I found the steep readme on github helpful:
gem install steep
I ran steep init
and it created a Steepfile
file, which I modified as shown.
target :pizza do
signature "sig"
check "*.rb"
end
I was curious if steep
could help me find errors. I created an app.rb
file that deliberately included a type error.
require_relative "./pizza"
# Correct type should be: Array[String]
# pizza = Pizza.new(["Pepperoni", "Green Peppers"])
# Incorrect type is passed instead: String
pizza = Pizza.new("Coffee")
puts pizza.to_s
Check for type errors:
steep check
Success! Well, success in that it found the error I deliberately put in
app.rb:4:18: ArgumentTypeMismatch: receiver=singleton(::Pizza), expected=::Array[::String], actual=::String ("Coffee")
Closing thoughts
I’ve tried Sorbet briefly too, and I’m not sure yet whether I prefer inline type annotations or external rbs files. Also, hiding the type annotations in a ‘sig’ folder seems a bit odd. I’m used to .h files being in the same directory as their counterpart, and rbs files seem similar to a header file to me. I do love the rbs syntax though.
Kudos to all the developers working on improving Ruby and making the ecosystem richer with type checking. Thank you!