Hacker News new | past | comments | ask | show | jobs | submit login

Didn't realize Square was interested in Ruby type checking, just like their competitors over at Stripe. Lots of money riding on Ruby, I guess :)

It does seem useful to have a _standard_ for type definitions - RBS as the equivalent to a .d.ts file - as that allows for different type checking implementations to use the same system under the hood. This was a big problem for Flow, and why it lost the fight as soon as TypeScript's definitely-typed repository started gaining momentum - users wanted to use the type-checker that they knew had definitions for the libraries they used.

On the other hand, RBS as hand-written seems rather dangerous, to me. Nothing wrong with using them to define previously-untyped external code, as long as you know the caveats, but I think you really want to have definitions generated from your code. Sorbet cleverly (and unsurprisingly, given it's Ruby) used a DSL for definitions in code, which had the (excellent) additional boost of runtime checking, so you actually could know whether your types were accurate - by far the biggest pain-point of erased-type systems like TypeScript.

Given that Ruby 3 was supposed to "support type checking," I'm surprised that it does not seem to have syntax for type definitions in code, and instead will focus on external type checking. I might be missing a piece of the full puzzle not covered in the blog post, however.




> I'm surprised that it does not seem to have syntax for type definitions in code

This is a big disappointment to me, one of the main advantages of static typing is that it can make code much easier to understand when types are added to non-obvious method parameters.


It also leaves a big question-mark over how this fits into the REPL, and how we might create type definitions dynamically, since many classes in a running Ruby application are conjured by frameworks.


At the moment, I’m disappointed in Sorbet’s capabilities‡, and it’s definitely not usable to me for the libraries that I maintain. I will (theoretically) be able to use Steep with _zero_ negative impact on deliverability.

https://github.com/sorbet/sorbet/issues/3252


Surely a good IDE such as RubyMine would be able to display the type in reponse to, say, a mouseover?


RubyMine tries but it certainly works less well than PyCharm and GoLand.


But that is because it lacks type information. With type information added via RBS (or whatever other mechanism), they'd be able to parse that.


Go is statically typed.


RubyMine works great if you add yard type docs to your code.


Is it checking those types or just reporting then?

I’ve seen so many instances where yard doc has the wrong return type or misses a return type that I rarely trust it.


It doesn't do typechecks, no. You can use solargraph (https://github.com/castwide/solargraph) for that.


For other readers: "Sorbet" refers to https://sorbet.org/, Stripe's Ruby type checker.


apologies, meant to add that as a link when I referenced Stripe in the first sentence!


> On the other hand, RBS as hand-written seems rather dangerous, to me. Nothing wrong with using them to define previously-untyped external code, as long as you know the caveats, but I think you really want to have definitions generated from your code.

Isn't the point that you run the type checker on your own code and it checks that it implements the signature correctly? Having a mismatch between the code and the signature will give a type error. How is this different from how Sorbet works?


Flow supports importing type definitions for third party untyped libs. And in fact did a better job at being integrated in current projects.

Flow lost because the compiler was in really bad shape, slow and frequently crashing. Also their equivalent repository to DefinitelyTyped would ignore PRs for months and years and afaik still does.

It's like it was somebody's toy project and its author eventually lost interest.

It's a pitty because TypeScript still has unsound generics. But Microsoft know how to make dev tools and maintain them.


> On the other hand, RBS as hand-written seems rather dangerous, to me. Nothing wrong with using them to define previously-untyped external code, as long as you know the caveats, but I think you really want to have definitions generated from your code.

That’s sounds like what type-profiler, mentioned in the article, is for; it's an experimental project which,if successful, seems destined to be part of Ruby’s bundled command line tooling, for generating type signatures from code.

If you mean you want type signatures embedded in code source files rather than in separate files, they seem to be taken a documentation-annotation approach, with YARD documentation format expressly called out as a mechanism to bed typing in source files. That's probably cleaner than further cluttering Ruby’s syntax with annotations.

> Given that Ruby 3 was supposed to "support type checking," I'm surprised that it does not seem to have syntax for type definitions in code

The support seems to be that, at a minimum, that it will have a standard for type definitions and provide them for Core and Stdlib and have command line tooling for working with type definitions. Which is, I would say, significante support.


I also believe that, at some point, Ruby _will_ support RBS/steep format in the source code, but the advantage to something like .rbs files is that projects that need to support 2.x and 3.x don’t have to maintain two different versions of the code.


It must be a very easy next step to allow type declaration inline with the code, for example as comments of special format, or maybe some meta-fields / annotations (I'm not a rubyist so don't know whether the language allows associating custom meta information with program elements).


> It must be a very easy next step to allow type declaration inline with the code

Updating Ruby’s already notoriously complex syntax to support type annotations while keeping existing Ruby code valid with it's existing semantics is...not a very easy step, I suspect.

Annotations in documentation is a more viable way of integrating type definitions into program source files.


This is the first I have ever heard of ruby syntax as notoriously complex. If anything it’s usually the opposite. I would love to read why people say that about ruby.


Ruby syntax is designed to be easy to use and flexible for humans; the decisions taken in pursuit of that makes the actual syntax itself quite complex and difficult to parse and, more to the current point, difficult to modify without breaking things that are currently valid Ruby.


Yeah, there are so many alternate syntaxes, shortcuts, and ambiguous statements in Ruby; just reading through Matz’s reference book on Ruby was a trip for me.


Ruby is human-friendly at the expense of a machine-friendly syntax. Writing a machine parser for Ruby is awful.

I guess a language on the opposite side of the spectrum would be Lisp-likes, which are brain-dead simple to come up with a generative grammar for, but a little hard on the eyes.


That is a fallacy in language design. Humans do not have an algorithmic shortcut for parsing; if it's hard for the machine, it's hard for the human.

For short chunks of program text, we can probably rely on our natural language abilities to some extent. Those capabilities allow us to deal with transformational syntax, and ambiguities. So that is to say, we have a kind of general parsing algorithm that is actually way too powerful for programming language syntax, but which only works over small peepholes. Most speakers will not understand (let alone be able to produce) a correctly formed sentence that is too long or too nested. It's as if the brain has a fixed-size pattern space where a sentence has to fit; and if it fits, then a powerful pattern matching network sorts it out. Whereas a programming language parser is unfazed by a single construct spanning thousands of lines, going into hundreds of levels of nesting; it's just a matter of resources: enough stack depth and so on. As long as the grammar rules are followed, and there are resources, size makes no difference to comprehension.

When reading code, people rely on clues like indentation, and trust in adherence to conventions, particularly for larger structures. Even relatively uncomplicated constructs have to be broken into multiple lines and indented; the level of syntactic complexity that the brain can handle in a single line of code is quite tiny.

We also rely on trust in the code being mostly right: we look toward understanding or intuiting the intent of the code and then trust that it's implementing that intent, or mostly so. If something looks ambiguous, so that it has a correct interpretation matching what we think we understand to be the apparent intent, and also has one or more other interpretations, we tend to brush that aside because, "Surely the code must have been tested to be doing the right thing, right? Furthermore, if that wrong interpretation is not actually right, the program would misbehave in certain ways (I guess), and in my experience with the program, it does no such thing. And anyway, this particularly code isn't even remotely near the problem I'm looking for ..."


The idea that human and machine language parsing have any underlying similarity is amusing but pretty absurd. It depends upon the idea that we're somehow doing the "same essential thing", which we are not. Humans do not translate text to serial machine instructions for a processor. They do many things with text, but that is (very seldom) one of them.

I meant literally that Ruby is easier for a human to read for comprehension than say, x86 assembly, which it is. Ruby requires (however) substantially more complex parsing logic to machine parse (translate to machine instructions), because Ruby syntax tolerates an almost absurd amount of ambiguity. This distinction holds when you compare Ruby to many common programming languages. Lisp is an excellent example of a high-level language that can be parsed with minimal complexity. I can teach an undergraduate to build a Lisp parser in a day, but it would take weeks to get someone up to speed on a Ruby parser.

This was not posited as an essential tradeoff in programming languages (if I came off that way, my apologies). Ease of human readability is probably orthogonal to ease of machine parsing.


If you think that you have an algorithmic shortcut when parsing code, try cramming even a moderate amount of code into a single line with no indentation, and go by the token syntax alone. You will find yourself doing ad hoc parsing: scanning the code for matching tokens to extract what goes with what to reconstruct the tree structure.

Humans don't have a magic algorithmic shortcut. If I give you scrambled word decks of various sizes to sort manually, the best time performance you will be able to show will appear as an N log N curve. Maybe you can instantly sort seven objects just by looking at them, but not 17.


That would only be parsing Lisp s-expressions, which is a simple data syntax. But it's far from the complete syntax, which btw. is basically not statically parseable, since Lisp syntax can be on the fly reprogrammed by macros.


Back in the 1.8.x era, the Ruby parser was already 6k lines, even using a parser generator.

The grammar is notoriously complex in ways that most users of the language thankfully do not have to worry about. But it does make extending the syntax quite hard.


I did not suggest to update ruby syntax


I mean, the most obvious solution would just be to unify Ruby's basic syntax with the RBS syntax shown in the OP. This format already looks like a Ruby class definition with the method bodies omitted and some simple "-> type" and ": type" syntax added. I think that's why people find the separation confusing.


> This format already looks like a Ruby class definition with the method bodies omitted and some simple "-> type" and ": type" syntax added

The thing is that much of it is perfectly valid Ruby code with wildly different semantics already, so, no, without breaking a lot, you can't unify it with Ruby syntax.


I'm not familiar with Ruby at all, but presumably it'd be possible to at least generate stubbed out definition RBS files with type inference.


It is, and checkers like Steep and Sorbet can infer these types. We're currently playing with the idea of deriving from documentation like YARDoc as well.


https://github.com/AaronC81/sord is one attempt at this




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: