7 ms·
> The best things are still a reasonably small core of concepts, with the development team working hard to “factorize” any new feature into those concepts, and
by lolinder 3y ago
> The best things are still a reasonably small core of concepts, with the development team working hard to “factorize” any new feature into those concepts, and the flexible syntax that is instrumental for making the core small
Incidentally, this is actually the definition of syntactic sugar, from a language design point of view. People often use it derogatorily to refer to bits of syntax that they simply don't like, but as a term of art it refers to syntactic elements that are introduced in an effort to provide new ergonomics—new ways of working in a language—without altering the language's fundamental primitives. As the article notes, in theory this method of language extension should actually keep the language simpler than if these elements were introduced as new semantic constructs.
There are a few places where this goes wrong in practice.
The first is that you end up accumulating a lot more syntax than you would if each syntactic extension to the language required a new semantic basis. It's easy to support syntactic sugar on a compiler or interpreter backend, so a language that is built on this small-core-with-sugar model doesn't have any natural forces urging it towards simplicity. To an experienced developer who watched all the layers of abstraction accumulate this doesn't pose much of a problem, but it's not always obvious to a newcomer what all this syntax desugars into, so for them it may as well be a new semantic element!
The other major place where I've seen sugar go wrong is that it rarely gets extra tooling to support it. A brand new semantic element will naturally need to be supported at every level of the language's stack, but the entire point of adding sugar is to avoid that extra work, so the language developers often just assume that the existing tooling will suffice. This leads to rough edges in things like stack traces and the debugger, because the code that actually gets executed only bears a loose resemblance to the code that the developer wrote.
It should be possible to avoid these pitfalls and have a tight language with a small basis and solid tooling, but it requires a lot more discipline.
- emodendroket 3y agoWell, if you're using Ruby in the first place, you're probably not the kind of person who cares very much about how much the tooling can help you, because even the best-in-class tools are primitive compared to what you get for other languages.
- psychoslave 3y agoCan you elaborate on this? Apart tools requiring static typing as a foundation, I am not sure what tools you are thinking of.
- Arnt 3y agoI agree. I work with ~5 of the top languages, don't see that difference, would like to hear an explanation. Even if I disagree with it in the end, I'll learn something.
- dtech 3y agoEven something as basic finding all references to a variable or function doesn't work very well in most IDEs. Shoutout to the folks behind Sorbet for making an awesome LSP for VSCode [1]. It comes close to the tooling expected nowadays, bit is still very far behind even popular dynamic languages like JS or Python. [1] https://sorbet.org/docs/vscode https://sorbet.org/docs/vscode
- emodendroket 3y agoTooling has gotten pretty sophisticated at working around that limitation for languages like JavaScript or PHP but Ruby lags way behind. The different between what RubyMine and PHPStorm or WebStorm can do is strikingly obvious to anyone who's worked with both. Or at least, if that's not the case anymore, that's how it was when I last compared the two environments.
- lloeki 3y agoHmm, as a Ruby dev my experience differs vastly. In a context where I'm working on a few languages regularly and among a larger set of languages that coworkers are in charge of, I can see how Ruby has solved a ton of problems that other languages are still struggling with. - package management: bundler/rubygems has been a sa-holved problem for the longest time whereas npm/yarn is an unholy mess, python is only beginning to see the light after refusing for so long, go mod is kind of a step in the right direction but still falls short in others, cargo is an outright copy but tries too hard to also be rake, a distinct problem making it a kintchensink... Gemfiles being descriptive, one can generate lockfiles from their platform including information for a foreign one and it's going to resolve consistently, including corner cases like this gem has a transitive dependency on a ruby extension and this platform has a binary gem at that version but that platform can build from source at that other version so I'm going to pick the one that's consistent and respects the constraints that have been described, or bail out and yell at you. This makes using semver-like squiggly a breeze and a non-event. - build tools: scons, cmake, ninja, doit & al. feel both incredibly complex and limited compared to rake. Every single time build files end up becoming obscure behemoths. When they don't they're incredibly focused on one thing but to make them to anything else they become a hodgepodge of additional external scripts and have to rely on so many tricks and hacks, which is why I used to stick to make. People seem to also have forgotten that rake can perfectly replace make as well, but rake can do so much more and still present a consistent interface and task implementation. - testing: minitest/test, minitest/spec, spec are running circles around any other test framework. The composability of reusable contexts, nested cases, and shared examples allows one to cover a ton of behaviours with very little test code that is eminently descriptive. Just the other day I did a deep reflector of some part of the code, and I only had to change a bunch of helpers as to how things should operate, and the entirety of my functional test suite was literally a zero-line diff. - talking to browsers: capybara is still my go-to to automate browsers, whether in tests or not, headless or not. It's just way too easy. - linting: Rubocop has a metric ton of tunable checks to enforce your preferred style and optionally auto fixes a lot of violations, standardrb takes a tab from gofmt by giving you some sane community defaults for Rubocop and thus autoformats by default. Rubocop is easily extensible, either you pull things and can lint rakefiles, Rails, spec and whatnot, or if you have special cases you can write your own cops. - typing and LSP: forget Sorbet, RBS+Steep is the way. RBS can generate typed skeletons either statically or dynamically using typeprof; ideally if you have good coverage you could run tests using typeprof and have automatic typing generation that can then be used entirely statically. Then the `rbs` command then allows you to statically ask a ton of questions about types but it's not a linter by itself, that's where Steep comes in. Steep comes with a LSP and it Just Works: autocompletion, inline type information, and so on. Type signatures can be packaged within gems, but if they're not there's rbs_collection, which also integrates with bundler. So I'm not too sure what you're referring to as "tooling" that would be primitive when compared to other languages when in my mind it's the other languages that constantly play catch-up ;)
- pmontra 3y ago> To an experienced developer who watched all the layers of abstraction accumulate this doesn't pose much of a problem Well, I'm fairly experienced because I've been using Ruby since 2005 almost only with Rails plus some small scripts that I used to write in Perl, and yet I'm not up to date with all the new syntax and sugar that accumulated in the last years. There are at least two reasons for that. 1. Working with Rails means that we tend to use only the features that make sense to use inside a Rails app. An extreme example: I experimented with Ractors but if they have a place in Rails app it's somewhere inside rack or db drivers, or maybe something like Sidekiq. They have no place in code that runs in the short lifetime of a web request. 2. Each customer use a different version of the language and of Rails, also among different apps of the same customer. They stick to those versions for years, usually until they approach end of life. That makes hard to remember which feature you can use in which project, so one tends to use the oldest common feature set. Example: I never saw anybody using pattern matching, which by the way changed between 2.7 and 3.x.