Yelling at a rocket after launch: checked optionals

Aye, and this isn't even theoretical, it's a very real issue that comes up often with Sorbet, a static type checker for Ruby.

Ruby doesn't have properties. An object's instance variables are only exposed through methods. To the outside world, there's no way to distinguish between a method that's just a dumb reader (that returns consistent values between calls) vs a method that does more complicated things (and returns different values on each call).

Thus, Sorbet is forced to pessimistically assume all method calls can return a different value from one call to the next, making this code fail type-checking:

x = !maybe_int.nil? && (2 * maybe_int)

(Ruby lets you omit the parens, so maybe_int is a method call, as if you wrote maybe_int().nil?)

The fix is to introduce a new local variable:

tmp = maybe_int
y = !tmp.nil? && (2 * tmp)

Where Sorbet can be confident that the value of tmp stays consistent from one read to the next.

5 Likes