4 ms·
FWIW, you can use inline syntax with Sorbet[0], one of the two typecheckers that will work with the RBS format (the other being Steep, which does not have inlin
by rattray 6y ago
FWIW, you can use inline syntax with Sorbet[0], one of the two typecheckers that will work with the RBS format (the other being Steep, which does not have inline syntax).
Here's a full example, complete with a typo, based on the example in the blog post: https://bit.ly/3hMEMSp https://bit.ly/3hMEMSp
Here's a truncated excerpt to get the basic idea across:
# typed: true
class Merchant
extend T::Sig
sig {returns(String)}
attr_reader :name
sig {returns(T::Array[Employee])}
attr_reader :employees
sig {params(token: String, name: String).void}
def initialize(token, name)
@token = token
@name = name
end
end
Disclaimer, I used Sorbet while I was an employee at Stripe. I found it to be a terrific typechecker. It's also just absurdly fast (most of the time).
[0] https://sorbet.org https://sorbet.org
- baweaver 6y agoSorbet was written in C++ and is a great piece of work, Stripe did a great job with it. It does have some issues as soon as someone gets into the magic weeds with metaprogramming like Rails does. Disclaimer: Working at Square, have friends at Stripe, enjoy both type checkers.
- cheez 6y agoThis syntax is horrible. I'm surprised they didn't just copy Python's typing syntax.
- Conlectus 6y agoAn important limitation here is that it needs to be valid Ruby syntax as well, since this was added without/before official Ruby typing support.
- Trasmatta 6y agoI believe one of their guiding principles was that they wanted all the syntax to be valid Ruby, because they did not want it to become a separate Ruby interpreter. So they were pretty limited in the syntax available to them.
- cheez 6y agoI'm not sure a separate interpreter is necessary but a preprocessor could remove the notations perhaps.
- deleted 6y ago[deleted]
- jrochkind1 6y agoOK, but if we're going to have .rbs, why not just modify the ruby syntax to allow .rbs-style types inline? Especially becuase .rbs already looks like class and method definitions without the bodies. So... just add the bodies. class Merchant attr_reader token: String attr_reader name: String attr_reader employees: Array[Employee] def initialize(token: String, name: String) -> void # actual method body end def each_employee: () { (Employee) -> void } -> void | () -> Enumerator[Employee, void] # actual implementation body end end It seems like they are trying to support existing competing work... but i'm not sure any ruby users actually want that. I prefer this .rbs to sorbet all around, and would prefer it inline.
- rattray 6y ago> why not just modify the ruby syntax The Ruby syntax is too complicated to allow for changes like this to be backwards-compatible. For example, `attr_reader token: String` is valid ruby today – that's the same as `attr_reader(:token => String)` which somebody might be doing in the wild, since you can override `def self.attr_reader`. Similarly, `def initialize(token: String` clashes with the definition of keyword arguments.
- jrochkind1 6y agodoh! good point. I am not able to spin that into "And besides it's better to force it to be in two files anyway!", I don't think it is, but I guess it's not so easy to do different.
- TylerE 6y agoHeader files suck. Anything is better than a separate file.
- dragonwriter 6y ago> Anything is better than a separate file. I dunno. Massive breakages of backward compatibility in an established language may not be better than that.
- kibibyte 6y agoOne thing I never really figured out with Sorbet is how it would work if I wanted to distribute a gem with type checked code. A typed gem would necessarily have to depend on the sorbet gem. Wouldn't this mean library users have no choice but to opt into type checks always being run in this library? (Is this why sorbet-runtime exists?)
- vidarh 6y agoYou can sort that out easily by doing something like: module T module Sig def sig *args end end # You'd need to stub out a few more things here. end begin require 'sorbet-runtime' rescue LoadError end Basically as far as what I can tell from just having briefly looked at Sorbet, you could quite easily stub out the bare minimum to allow people to choose whether to pull in the full thing or not. It'd be nice if they provided a gem that did that.
- rattray 6y agoYeah, the gem would depend on sorbet-runtime, and the library author could configure sorbet to not run any checks in production if desired (or to have any errors log instead of throw). You can configure things like this globally and/or for each method call. Eg; # turn off all runtime checks T::Configuration.default_checked_level = :never # turn off runtime checks for one method sig {returns(String).checked(:never)} def foo; :wont-raise; end Docs are here: https://sorbet.org/docs/runtime#runtime-checked-sigs https://sorbet.org/docs/runtime#runtime-checked-sigs Personally if I were authoring a gem I'd leave the runtime checks on except in hot paths, so my users get quick feedback when they pass the wrong thing. In any case, the library author can get the benefits of static and runtime typing, and their users will get nice static typing if they use sorbet. Users also get nice runtime typing for the library if the author chooses to leave it on for them. The overhead is usually small.