Sorbet Does Not Have FixMe Comments

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 sorbet.run

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(T.untyped).void}
def foo(x)
  y = x.even?
# ^ type: T.untyped
  z = !y
# ^ type: T.untyped
end

→ View on sorbet.run

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, callingDid you know that !x in Ruby is syntactic sugar for x.!(), which means that you can override ! to make it do something else?

! 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.