Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provide way to disable T.let / T.cast for production setups and debugging #3279

Open
md-work opened this issue Jul 13, 2020 · 2 comments
Open
Labels
enhancement New feature or surprising current feature runtime Related to the sorbet-runtime gem unconfirmed This issue has not yet been confirmed by the Sorbet team

Comments

@md-work
Copy link

md-work commented Jul 13, 2020

It seems there's no way to disable T.let and T.cast globally.
This might be helpful when running code in production setups because of two reasons:

  • T.let / T.cast may be time consuming (see below)
  • production setups may want to ignore typing errors, to reduce risks from incorrect Sorbet code

Additionally a miminal T.let / T.cast codepath might be helpful when debugging (if there's a method call inside T.let(..., which you want to step into). Sadly sorbet-unwrap won't help here. (see also AaronC81/pry-sorbet#5)

 

To solve this problems for method signatures (sig { ... }) one can simply set:
T::Configuration.default_checked_level = :never

There seems to be no such setting for T.let / T.cast.
There's just a hacky workaround I know of.
QUESTION: Is there any counter argument to do this in productive code?
Note: The local checked setting isn't helpful to set a global behavior.

module T
  def self.let(value, type, checked: true)
    value
  end
  
  def self.cast(value, type, checked: true)
    value
  end
end

 

Example: slow

require 'sorbet-runtime'


class MyTest
  attr_reader :x

  def set_typed_x(new_x)
    @x = T.cast(new_x, T::Array[Integer])
  end

  def set_untyped_x(new_x)
    @x = new_x
  end
end


test = MyTest.new

time = Time.now
ary = Array.new(50_000_000) { |i| i }
puts('initializing array: ' + (Time.now - time).to_s)
# 2.08708646

time = Time.now
test.set_typed_x(ary)
puts('set_typed_x: ' + (Time.now - time).to_s)
# 4.434147138

time = Time.now
test.set_untyped_x(ary)
puts('set_untyped_x: ' + (Time.now - time).to_s)
# 3.451e-06

 

Example: ignore problems when introducing Sorbet

require 'sorbet-runtime'


class MyTest
  attr_reader :x

  def initialize(x)
    # Someone might wrote down this very fast and forgot T.nilable
    # But nil might be totally fine for the applications task.
    @x = T.cast(new_x, String)
  end
end


test = MyTest.new(nil)
puts(test.x.inspect)
# Is considered to output "nil" in this case.
@md-work md-work added enhancement New feature or surprising current feature unconfirmed This issue has not yet been confirmed by the Sorbet team labels Jul 13, 2020
@Morriar Morriar added the runtime Related to the sorbet-runtime gem label Aug 19, 2020
@paracycle
Copy link
Collaborator

@md-work I think both of your concerns have solutions that can be used today:

  1. Slow cast/let is solved by a recent change in sorbet runtime that stopped checking items in enumerable types. So your example above should be working much faster now.
  2. Ignoring problems when introducing Sorbet has been possible without turning off typechecking. This gives you the best of both worlds; you still get runtime type-checking errors logged but they don't break your code.

At Shopify our Sorbet initialization looks like this:

require "sorbet-runtime"

if ENV['RAILS_ENV'] == 'production'
  T::Configuration.default_checked_level = :never

  error_handler = lambda do |error, *_|
    # Log error somewhere
  end

  # Suppresses errors caused by T.cast, T.let, T.must, etc.
  T::Configuration.inline_type_error_handler = error_handler
  # Suppresses errors caused by incorrect parameter ordering
  T::Configuration.sig_validation_error_handler = error_handler
end

@md-work
Copy link
Author

md-work commented Aug 24, 2020

@paracycle

[...]
At Shopify our Sorbet initialization looks like this:
[...]

Thanks!
That's very helpful.

 

But what about stepping into a method when debugging?

x = T.cast(method_i_like_to_step_in, String)

I'd like to have a way where I don't need to step through the T.let code to enter the method_i_like_to_step_in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or surprising current feature runtime Related to the sorbet-runtime gem unconfirmed This issue has not yet been confirmed by the Sorbet team
Projects
None yet
Development

No branches or pull requests

3 participants