I'm seeing comments asking "why a separate file?" I watched a video last night from the creator of the language (at https://www.youtube.com/watch?v=IhTXDklRLME) where he addresses it.
At 12:20 minute mark he basically says he doesn't like annotations, and calls the separate file a compromise several times.
At 19:46, he admits it's hard to maintain separate files.
At 20:57 he says annotations are not DRY, would change the language, and aren't necessary for performance (points to V8/Chrome). At 23:10 he says no to annotations because he thinks one day the tools will be smart enough to give you the same guarantees/diagnostics without them, so at 24:50 he concludes that he doesn't want to have to remove annotations when that comes to pass.
(don't shoot the messenger! I don't necessarily agree with these conclusions...)
That's a pretty appalling set of justifications. I don't know of any type systems with perfect type inference that are still useful for fully exploiting types.
Crystal is the closest I've seen to match that. It's type inference is the most robust I've seen, you can write it almost like Ruby and still get full static type checking. I prefer to still use annotations fairly often with it though.
The trade off for that powerful type inference is a slow compiler.
> The trade off for that powerful type inference is a slow compiler.
When run in the default (i.e. no release, debug) mode, build time is ok, especially for rebuilds when only one file has changed which is less than a second. Release builds are slow (see my PDF).
You can go really far in Haskell without specifying a single type. I haven't done so in a long while, but I'm pretty sure explicit types in Haskell are only necessary when you're trying to lock down an API or are outputting to something generic.
And how are the justifications appalling? He thinks annotations are a detriment to the expressiveness of the language, and Ruby prides itself on being expressive.
Locking down an API is a critical feature of a type system, though, and beyond that, there are absolutely language extensions in Haskell that are not inferrable, in addition to polymorphic recursion. OCaml is the same way: the inference is very good, but not total.
As for being a reduction to the expressiveness of the language, I think Haskell is proof that you can have annotations without reducing the expressiveness of the language. Type annotations in Haskell and Idris don’t even interfere with value-level syntax!
As an alternative, Sorbet supports inline typing, separate files via .rbi (for typing external libs, etc), and I believe will support reading in .rbs files as well: https://sorbet.org/blog/2020/07/30/ruby-3-rbs-sorbet
It has some limitations (arguably good - it's harder to do shady metaprogramming if you want type safety) but I've found it very productive and useful in my large code base.
Wow types in a different file... This is like C++ but worse...
Is this because Ruby is extremely difficult to parse, to the point that nobody wanted to mess with the parser?
I remember reading a comment a while back from the creator of a Ruby parsing library and he said the language design is arbitrary and extremely difficult to parse correctly
I don't think that's not the reason at all, features are regularly added to ruby with much more complicated consequences for the parser.
I would speculate the reason is that it's a very consequential change for the user, and they don't want to commit to it yet. I bet Ruby 4.0 will have optional inline types, if it turns out RBS are as functional as is expected.
Not really, .ts files can have types in the source code. .d.ts lets you type external JS code which is useful for untyped dependencies, as is the case here, I imagine. But within your own project having types be part of the actual declarations is much better.
Those are only used to retrofit types on external libraries that don't have them. All types within project are in the source.
.d.ts is a way to add types to source you don't control.
Ruby should have gone the path of typescript where .d.ts generation and type stripping is done by a tool you run when you publish your library. That way you can work with types in your code but export it to plain js that others can use with or without tyoings
Not with truly independent parsers; as I understand, most (maybe all) use, either directly or by way of a direct translation, parse.y from MRI.
> and on top of that rubocop has an independent parser.
Rubocop (or, rather, the parser gem it depends on) seems to have an independent lexer and a parser that is a Bison translation (maybe even mechanical, I'm not sure) of MRIs parse.y.
I’m not too familiar with ruby but I’m curious why they didn’t just add optional types after the variable names. Such as in Kotlin, Scala, Swift like “var x:Int”? I thought this is how Python was adding types to its language. I could see this though fundamentally changing the philosophy/design of the language but I think dynamic languages are heading toward type safety anyway.
Probably because that would break existing Ruby code. Python added optional type annotations to the syntax but they are optional so it (mostly) does not affect old python code. I don't think they added a var keyword though.
These half typed approached are IMHO ultimately disappointing. It gets you some of the advantages of a type system but not the full robustness of a proper type system. Some languages do it better than others e.g. typescript is a bit more strongly typed than python 3. But ultimately you end up with a lot of compromises to allow legacy untyped code to still work. I think of this as a stepping stone for finding your way to a language with a proper type system. Once you get it and like it, you'll crave more and more is readily available in other languages.
I think the trend of type inference has sort of broken the "typing is too verbose for my taste" argument. I've converted a fair bit of javascript to typescript and you generally don't end up with more lines of code or less readable code (rather the opposite I would argue). And you do end up finding and fixing obvious type issues, glaring design flaws, etc. So, it clearly adds value at an arguably very reasonable cost. And that's before you consider IDE tooling and things like auto-complete, which add a lot of value in day to day use. Typescript in strict mode just feels like a better language than Javascript. I'm currently doing frontend development with Kotlin-js and the Fritz2 framework, which is awesome. It does feel like a step up from typescript + react but definitely not for the faint of heart if you are squeamish about early adopter stuff.
This new ruby typing system looks like an attempt to keep the language fresh and relevant in the same way. It looks like it is a necessary step at this point. I guess it's a valuable tool if you manage a large ruby code base none the less. Not one of my problems luckily. I haven't used it in years and at this point it's not an obvious choice for me for anything new.
regarding the "separate files" issue: I see in the python world (that support both external .pyi files and "inline" annotations) some obvious things:
- there is space for every solution (sometimes is really ugly to read type-annotated .py files, sometimes it's very clumsy to look to the associated .pyi files for types)
- the "in-line" approach is gaining popularity.
- the "external files" ease the transition (there are packages that only exposes .pyi files to extends the work of the autors that don't want to deal with them)
Thanks I hate it.
I wish it was part of the same file, it's very tricky to keep two files in sync especially during early development, and in a mature project it becomes a chore.
I thought most languages were moving away from header files. I know typescript has similar files but that is out of necessity as it is added to a language that has no idea typescript exists
I basically stopped paying attention to the progress of this feature when it was announced it would use header files. I don't expect I'll use it.
I think it might be worth the headache with libraries - I think type errors are more useful when you cross the boundary into someone else's code. They're still useful within apps, of course, but personally I don't think I hit them often enough to make the headaches of a separate file worthwhile.
My hope is that this is primarily motivated by wanting to maintain backwards compatibility for source, and maybe as more of the ruby ecosystem moves to 3.x future versions will add an alternative way of doing type annotations inline.
There are a few reasons this wouldn't work for Ruby, mainly the reliance on metaprogramming in some of the more popular libraries.
For example, the ORM ActiveRecord reads the database schema at runtime and generates the getters and setters for each column for the appropriate class. The class itself doesn't have any code listing these columns. Where would the inline annotation go?
You can do this in Python as well, see SQLAlchemy's "reflection" capability for instance. You can even generate annotations at runtime if you wanted to (but there wouldn't be much point).
I'm pretty confused as to why you would want to have annotations baked into your source code anyway, if you are reading the database schema at runtime.
I know that, but are you going to read the database schema at "analysis time", i.e. before runtime and likely in a totally different environment from the runtime environment? IMO "reading the database schema at runtime" and "ahead-of-time static typing" are incompatible desires, except in a language that supports runtime access to the type checker.
The inline annotation could go anywhere you want it to - in the Rails model example, in the model file. You just need a way to declare methods without defining them.
Fast forward one year to see the posts about how much time this saved Company X, and then fast forward one more year to the posts about time saved after removing RBS.
The killer is literally the first highlighted piece of text in the article:
> RBS is a language to describe the structure of Ruby programs.
I mean just let that sink in. This isn't going to work.
At 12:20 minute mark he basically says he doesn't like annotations, and calls the separate file a compromise several times. At 19:46, he admits it's hard to maintain separate files. At 20:57 he says annotations are not DRY, would change the language, and aren't necessary for performance (points to V8/Chrome). At 23:10 he says no to annotations because he thinks one day the tools will be smart enough to give you the same guarantees/diagnostics without them, so at 24:50 he concludes that he doesn't want to have to remove annotations when that comes to pass.
(don't shoot the messenger! I don't necessarily agree with these conclusions...)
(edit: tried to make the summary clearer)