←back to thread

177 points bartekpacia | 4 comments | | HN request time: 0s | source
Show context
phendrenad2 ◴[] No.45103164[source]
It's been a long time since I last used RubyMine, but I always felt that it was the weakest of the JetBrains tools. And not because JetBrains didn't try hard enough, but because Ruby just doesn't offer a lot of opportunities for an IDE to take advantage of.

I ended up cancelling my subscription over some trivial thing (I think it was the fact that I couldn't quite get the IDE to preserve the indentation of a file. It was an all-or-nothing global setting, but I work on codebases that might have a 4-space indent HTML file and a 2-space indent HTML File in the same directory, and the IDE was ignoring the current style of the file and using whatever indent level I had configured).

replies(4): >>45103607 #>>45103627 #>>45103891 #>>45104910 #
ryandv ◴[] No.45104910[source]
In general it's impossible to "find usages" or "go to definition" when the language not only fails to equip IDEs and tooling with the static typing information that would grant definitive answers to such questions; but even goes further and allows methods to be redefined or even synthesized and defined, for the first time, at runtime, with no corresponding source location or file. Method lookup and dispatch are fully Turing complete (you can `#send` anything, including fully dynamic method names, and respond to any message with arbitrary logic in `#method_missing`), and you can even redraw the method lookup chain and inheritance hierarchies at runtime (includes, mixins, module prepend, eigenclasses et al).

This is not a failing of JetBrains tooling but rather a pervasive language smell and consequence of Ruby philosophy.

replies(3): >>45105119 #>>45105413 #>>45105632 #
danmaz74 ◴[] No.45105413[source]
"Find usages" and "Go to definition" work very well in RubyMine in my experience, at least in normal Rails projects - I use them all the time with command-click.
replies(1): >>45105518 #
ryandv ◴[] No.45105518[source]
Anecdata that something is effective in 98% of cases is different from provable static verification that works 100% of the time.
replies(1): >>45106872 #
uticus ◴[] No.45106872[source]
> In general it's impossible to "find usages" or "go to definition"

> Anecdata that something is effective in 98% of cases is different from provable static verification that works 100% of the time.

Ruby has its faults. But you're not making sense complaining first about the "in general" situation, then complaining about a 2% situation. And it is a 2% situation, I'll add my anecdata as verifying 98% (or better) it works really well.

Secondly, you're mixing concepts of static typing and metaprogramming by saying "the language not only fails to equip IDEs and tooling with the static typing information that would grant definitive answers to such questions." Static typing is not the solution, or at least not the only solution, to metaprogramming concepts. For example, if I statically define a Ruby object via RBS, I may still do the things you list that are problematic.

Thirdly, you're complaining about runtime issues making things difficult for the IDE while doing a lookup. What language and IDE do you prefer that does this so much better?

Lastly, I doubt this will help but just to make you aware there are specific rules about how this stuff works, for example https://ruby-doc.org/3.4.1/syntax/calling_methods_rdoc.html#...

replies(1): >>45107130 #
ryandv ◴[] No.45107130[source]
> But you're not making sense complaining first about the "in general" situation, then complaining about a 2% situation.

The use of "in general" has long established usage in mathematics to refer to properties that hold for all objects in a given collection [0] [1]. Thus, if 2% of those objects fail to satisfy these properties, you cannot say that such properties hold "in general."

> Secondly, you're mixing concepts of static typing and metaprogramming by saying "the language not only fails to equip IDEs and tooling with the static typing information that would grant definitive answers to such questions." Static typing is not the solution, or at least not the only solution, to metaprogramming concepts.

Propose alternate solutions then.

IDEs and tooling (let's leave LLMs and other stochastic methods aside for now) can only inspect the lexical, syntactic, and semantic (types) structure of source code in order to navigate and validate it. Thus, information that is absent from the lexical, syntactic, and semantic structure of the source code, is unavailable to the IDE and static tooling. Ruby, being a dynamically typed language, does not statically encode any structure regarding types, callgraphs/callsites/usages, definitions, or otherwise, in the syntax of the language alone. The behavior of a Ruby program - how its methods are defined, where they are defined, and which runtime objects invoke send what messages to whatever recipients, are only discoverable at runtime, because nobody bothered to put this information in at compile/typecheck time. You cannot discern information that simply is not there.

> For example, if I statically define a Ruby object via RBS, I may still do the things you list that are problematic.

Yes, and this is because RBS is not actually a sound typesystem [2] [3]. You can declare whatever fake types and interfaces you want in RBS; there is absolutely nothing stopping me from simply using `#define_method` to overwrite your implementation, destroying your static guarantees, or using other runtime metaprogramming facilities to delete your method, change its signature, or otherwise violate whatever other invariants you assumed you had when you wrote the static headers/type declarations and had them checked by the static verifier.

It doesn't matter if you statically verify the code, when the language offers innumerable opportunities to destroy those static guarantees, after they were established, at runtime.

> Thirdly, you're complaining about runtime issues making things difficult for the IDE while doing a lookup. What language and IDE do you prefer that does this so much better?

I already provided an example of C# ReSharper upthread [4]. C# actually provides the static typing information that the tooling requires in order to navigate the AST with deductive certainty, not loose approximations or heuristics as in RubyMine.

Out of curiosity, did you study computer science formally at a post-secondary institution, or did you learn from a bootcamp?

[0] https://en.wikipedia.org/wiki/Glossary_of_mathematical_jargo...

[1] https://en.wikipedia.org/wiki/Glossary_of_mathematical_jargo...

[2] https://en.wikipedia.org/wiki/Type_safety

[3] https://www.typescriptlang.org/play/typescript/language/soun...

[4] https://news.ycombinator.com/item?id=45105283

replies(2): >>45108382 #>>45115624 #
rpdillon ◴[] No.45115624{5}[source]
SLIME is a better approach for dynamic languages, IMO. Interrogating the actual runtime is more effective than trying to do static analysis.
replies(1): >>45119944 #
1. ryandv ◴[] No.45119944{6}[source]
In a Turing complete dynamic language I believe that some properties of the runtime would be undecidable. The advantage of static typing is that many (most?) type systems are sufficiently constrained (i.e. not Turing complete) such that the typecheck procedure can be assured to terminate.

Of course, with sufficiently expressive type systems you end up with Haskell "UndecidableInstances" or Coq dependent types or C++ compile-time tetris, [0] and now you are back to the same problem as with analyzing the runtime. Otherwise you may find it difficult to encode the desired properties or invariants into your type system, for lack of expressive power... tradeoffs abound, and so on.

[0] https://news.ycombinator.com/item?id=9813800

replies(2): >>45122029 #>>45122657 #
2. ◴[] No.45122029[source]
3. mdaniel ◴[] No.45122657[source]
Or my favorite alternative to that story: well, it's currently a str ... maybe it'll still be a str in 5 minutes, who knows
replies(1): >>45124656 #
4. ryandv ◴[] No.45124656[source]
In a dynamic language where code that produces such a side effect is admitted by the (lack of) compiler/typechecker, sure; however a static checker would probably ensure, before runtime, that said binding indeed refers to a str, now and forever.

But I realize now the point of ancestor's comment is that you don't have such a guarantee in a dynamic language - hence the tendency to lean more on REPL-oriented development, highly interactive live runtime environments, tight TDD feedback loops, etc...