4 ms·
I was excited for this because the premise made a lot of sense, but I think the examples demonstrate why we don't already do this in the ways listed. (Stick aro
by fl0ki 3y ago
I was excited for this because the premise made a lot of sense, but I think the examples demonstrate why we don't already do this in the ways listed. (Stick around because I'll list examples where we already do this in far more useful ways that people with poor tooling don't seem to know about)
Nesting parens and/or braces - Breaking things into functions and clear indentation within those functions is already a much clearer information channel. Color is nice and all, but it's no substitute for communicating abstractions and invariants. If nothing else, code should still be clear even to people using different tools or with color blindness.
Many of the suggestions, such as "Highlight all functions with try blocks" and "All functions more than 100 lines long", point to code quality problems due to leaky abstraction. If it matters to a function's caller whether it has 99 or 101 lines, or whether it can internally handle errors, something is desperately wrong. If you want to discourage such functions, we have linters for conditions like that, it shouldn't be a distraction to people maintaining the callers of such functions.
An example I almost agree with is "All functions that transitively call functions that make an http call", though I would generalize that to any network IO. Conventionally, in Rust such functions should now be async functions, and in Go they should take a `Context` for cancellation, both of which are visible at the call site, so idiomatic code is already clear in these ways. Again, it's a way we make things clear and explicit in the code itself, not just the highlight. (It's one of the reasons people blasted Scala's implicit arguments design; the saving wasn't worth the loss of clarity, highlighted or not)
"All lines last edited by a particular member of the team" - IDEs already have git blame views that provide much more information such as the commit comment and date. Either way these often fall down in practice; if you so much as change the indent level, it's your line now as far as the tooling is concerned.
Now for examples where we actually do this already and deserve to be credited:
JetBrains Rust Rover highlights unsafe functions very distinctly. unsafe{} blocks are allowed to call safe or unsafe functions, and this just shows you which of them are unsafe. That's a lot more useful than "All functions more than 100 lines long". I don't care how long it is, I do care if it could be the reason my project becomes the next vulnerability news cycle.
Unlike Rust and Java, Go doesn't have a way to limit a binding to at-most-once initialization, so "All variable identifiers we assign to twice" might actually be useful though in many cases it would just be ugly for no reason. For now though, JetBrains GoLand highlights shadowed variables, including in cases where they avoid bugs more than they cause them, but at least the right intention was there; whether a variable is shadowed is visually unclear in Go because := reuses an existing binding or makes a new one depending on the scope (unlike Rust's `let` which always makes a new binding even in the same scope), so the highlighting does provide information that's easy to miss otherwise. Where I disagree most is that the puke-green color makes it look like you're supposed to avoid it. Of course GoLand also highlights reused bindings specially too, with a neat underline that doesn't make it look like a problem to avoid.
I could go on but this is already a very long comment. I hope this is enough to demonstrate that many of the examples don't make a lot of sense because there are already better highlights or other tooling available, and tools already do use semantic highlighting for a few useful things without reducing the value of syntax highlighting. This is a well-understood idea in modern tools, it's just used more sparingly than suggested here.
- danjl 3y agoI like the "unsafe function" idea. More generally, it would be great if you could somehow show an "unsafe chance" metric, which would show lines that can cause multithreading issues. That is, rather than relying on the unsafe{} blocks, you would use some sort of Valgrind or similar output to generate a heuristic that measures the chances of shared data collisions or the like.