Sorbet Does Not Have FixMe Comments

February 11, 2020

Sorbet has no way to ignore an error on a specific line with a magic comment. This is different from all other gradual static type checkers I know about:

When I first joined the team, I was skeptical. But having seen it play out in practice, it’s actually worked great.

Instead of ignore comments, Sorbet has T.unsafe, which accepts anything and returns it unchanged (so for example T.unsafe(3) evaluates to 3). The trick is that it forces Sorbet to forget the type of the input statically. This confers the power to silence most errors. For example:

1 + '1'            # error: Expected `Integer` but found `String`
T.unsafe(1) + '1'  # no error

→ View on

In this example, Sorbet knows that calling + on an Integer with a String would raise an exception at runtime, and so it reports a static type error. But wrapping the 1 in a call to T.unsafe causes Sorbet to think that the expression T.unsafe(1) has type T.untyped. Then, like for all untyped code, Sorbet admits the addition.

All Sorbet-typed Ruby programs must grapple with T.untyped. Every Sorbet user has to learn how it works and what the tradeoffs of using it are. In particular, that T.untyped is viral. Given a variable that’s T.untyped, all method calls on that variable will also be untyped:

# typed: true
extend T::Sig

sig {params(x: T.untyped).void}
def foo(x)
  y = x.even?
# ^ type: T.untyped
  z = !y
# ^ type: T.untyped

→ View on

In this example x enters the method as T.untyped, so calling the method .even? propagates the T.untyped to y. Then again because y is untyped, calling1 ! on it propgates the T.untyped to z. There are plenty of reasons to both embrace and avoid T.untyped in a type system but the point is: Sorbet’s type system already has it.

Re-using T.untyped as the way to silence errors plays nicely with everything else in Sorbet:

The Sorbet docs bill T.untyped as the way to “turn off the type system.” By reusing T.untyped to supress errors, silencing one error means silencing them all, which is a win for simplicity.

  1. Did you know that !x in Ruby is syntactic sugar for x.!(), which means that you can override ! to make it do something else?

Read More

Intersection Types in Sorbet are Surprisingly Common

Conventional knowledge is that union types are common and intersection types are rare. But actually that’s not the case—intersection types show up in nearly every program Sorbet type checks thanks to control flow. Continue reading

What makes type checking Ruby hard?

Published on December 29, 2019

Surgery on Code from the Command Line

Published on July 30, 2019