The human typewriter, or why optimizing for typing is short-sighted
felixk15.github.io
felixk15.github.io
Type info is needed on function signatures mostly so people know how to call the thing. That's why Javascript and Python have acquired bolt-on typing systems. It's also why cross-function type inference hasn't caught on. It can be done technically, but it just confuses the humans.
Humans are not good at maintaining consistency between the thing right here and that other thing way over there. It's best not to design systems which require that.
They introduced some new syntaxes in recent versions, c#12 and 10(I think?):
List<Foo> foos = [];
Bar bar = new();
I expect that to replace implicit var declarations as the 'normal' way, especially as that's what VS code analysis suggests now.I don't use resharper, personally found it far too opinionated in what I felt were bad ways. Plus it often resulted in poor performance in VS.
For years I've used var pretty much exclusively, it's just easier than typing the type twice. Even after they introduced new().
With the new array-like declarations I'm considering switching to declaring the type on the left rather than the right.
Which, in the end, is all this syntax is, a pretty way to stop you needing to declare the type twice. And now you can either do it on the right of the expression like this:
var things = new List<string>();
Or on the left: List<string> things = [];
Although, one of the nice things about using var is that it makes it easy for your eye to scan a bunch of variables declared together as they line up.This poor Neovim user also has LSP with inline hints, hover and help like IDEs.
Vim/Emacs aren't just the dumb text editors anymore.
but I'm curious if LSP can write it for me why not just have compiler infer it?
Do you find it faster to context switch to that mode from typing flow? I don't always
The author certainly has that right, because the post steps on two programming religion landmines, from how I read it:
1. strict static typing (without type inference) is good. 2. code should be written to allow IDEs to enhance navigability, rather than written on the assumption that IDEs will be the sole provider for navigability.
I believe there is a point to be made in the "when we don't know what we're getting back, that harms navigability" camp. But as another commenter posted, there's a point to be made in the "when we overspecify what we're getting back every time, that can harm readability, too" camp.
I can't express where this balance is. It's somewhere between poetry and a legal document, the prose where you can really get into a good book and enjoy the world that the author presents. Some people really like the beauty of a short poem. Other people may require precise wording that leaves no room for enjoyment or interpretation. The rest of us can have the majority of fun somewhere in between.
Where that "in between" equivalent would be in my day-to-day programming, I'm not entirely sure, because what I'm writing could be a short script where brevity is vital (poetry-ish) vs some section of unfortunately highly complex code with lots of tests for edge cases (legalese), and all the other code where I'm still world-building and conveying ideas (prose). And I believe that complexity should be spelt out as precisely as it can in the code itself, rather than rely on the hope that somebody else is using the same IDEs and features as me. I've tried using type inference where it seems fine to use, and then spelling out the exact type that a variable wants where it isn't clear what might get returned, all in the same app, but it comes across as sloppily inconsistent in my mind. Ah well.
The code is listed as c++. However, it is using a style that will result in problems.
First thing that jumped out at me is using const char* for strings. Using this muddies ownership and doesn’t store the length of the buffer which can result in all sorts of fun. Add to that that many times paths are formed by string concatenation, and it is looking like a code smell.
Second, there is no RAII and just manual management of resources.
These problems are far more likely to cause issues that the use of type deduction which by now has pretty good tooling support and is used successfully in many languages. Of course, not using RAII, using raw pointers make it tricky, but you shouldn’t be using those like that anyway.
They are a feature of (and configured in) the LSP Server, so every editor that has LSP integration can display them. Still doesn't help when there is no LSP around.
I'd say "vim user" is already an insult in itself, so that doesn't matter at all ;)
I believe that the proper term is "vim disciple"
I kinda liked working with pytype at Google; it let me specify types when it's important either for correctness or documentation purposes, and ignore them when it's obvious.
My experience working with modern C++ is that there are a zillion horrible templated types that mostly get in your way when you're trying to do something boring. 'auto' is a useful way to clear out that messy boilerplate garbage and make your code clearer. But if it's not making your code clearer, then that's what code reviews are for.
LLVM has a well thought out stance on auto too: https://llvm.org/docs/CodingStandards.html#id29
That being said, there is a third option here:
- type `auto`
- have an autoformatter that replaces `auto` with `std::vector<std::string>::iterator` or whatever the abomination of a type you need is :P
I don't know, that is obscuring a lot of details. I think we really need it to be
std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::alocator<std::basic_string<char, std::char_traits<char>, std::allocator<char>>>::iterator
How would I be expected to understand the code if I didn't know what char_traits are used in the vector's allocator when opening the file in ed???Is it? In my experience it goes downhill with the square of the size of the project.
That one 500 line glue script that does one thing and interacts with just the standard library? No types, yeah baby!
That 50k line app partly written by some dude who doesn't work here any more? Now it's getting annoying.
More? Even worse.
- you write "auto" as the type.
- You run a script that updates your code and replaces "auto" with the appropriate type for you to review and keep in the code.
Hmm perhaps auto should be an IDE function and not a language feature? Type:
auto blih = *that_custom_collection_from_the_legacy_code_base->begin();
and when you press enter it replaces it with the proper type.
True story. I could use that right now :)
I also worked on a much smaller C++ code base for 9. It was very bad.
Types or not isn't the big thing here. It's having a good engineering culture.
But my experience is with different code bases mostly done by the same people across time. And in that case you start wishing for types on the larger projects...
Untyped, non-descriptive objects calling each other randomly, with no chance for the reader to figure out what is going on.
So in this case auto is harmful. In many other cases it isn't.
What's in the `request` object for this view? Whatever the fuck you want, your IDE won't help you, and even the documentation can't provide the full answer because half your stack stuffed things into there that you couldn't even guess.
I like to think of myself as a good programmer, but whenever I encounter code where I have to figure out what "x" means, when it could have been a speaking variable like "accumulative_duration_ms" I feel like someone is trying to be clever.
I see using auto in a similar light. Explicit return types are good. In fact there's a trend to retrofit typing into languages that don't have it. You showing me that you value not thinking about the return type over specifying explicit return types is a red flag to me.
If we as programmers should value anything, it is the time and mental resources of the person who has to read our code in the future — very often that person is going to be you, yourself.
Use auto when the local code shouldn't care about the type as a way of signifying that the local code shouldn't care about the type. It doesn't happen all the time, but it does happen sometimes.
Don't use auto when the code needs to care about the type.
auto introduces quite a bit complexity to already complex C++ code. It behaves completely differently in lambdas. Using it as the return types usually causes implicit and costly conversions. Using it with primitive types is basically a booby trapped temple for juniors who didn't burned numerous times and mere mortals who don't have time to learn cppreference by heart.
If I don't care about types, I would use a higher level language.
Yes a compiler will give warnings and whatnot, given you have a modern enough one and somehow constantly running it. It is costly in time though. CI builds are not free. Developer time is less so.
This comment is sort of ironic given the title of the piece. As a Vim user, I opened it expecting to be called out for optimizing for typing.
Starts with an interesting claim "don't optimize for typing", but then it completely fails to prove it, and confuses itself in thinking that `auto` is an optimization for typing.
`auto` is:
- A way to express types that are impossible or truly difficult to express, such as iterators, lambdas, etc
- A way to optimize reading, by limiting the redundancy
- A way to optimize maintenance, by limiting the amount of change brought by a refactor
The insistence on notepad or "dumb editors" is also difficult to grasp. I expect people reviewing my code to be professionally equipped.
Lastly the example mostly fails to demonstrate the point.
- There's a point made on naming (distinct from `auto`): absent a wrapping type, `dataSizeInBytes` is better than `dataSize`. The best way though is to have `dataSize` be a `Bytes` type that supports conversion at its boundaries (can be initialized from bytes, MB, etc)
- What's the gain between:
auto dataSet = pDatabase->readData(queryResult.getValue());
and DatabaseDataSet dataSet = pDatabase->readData(queryResult.getValue());
The `dataset` part can be inferred from the naming of the variable, it is useless to repeat it. The `Dabatase` is also clear from the fact that we read data from a db. Also, knowing the variable has this specific type brings me absolutely nothing.- Their point about mutability of the db data confused me, as it is not clear to me if I can modify a "shadow copy" (I suppose not?). I suggest they use a programming language where mutating something you should not it a compile time error, it is much more failsafe than naming (which is hard)
I'm sad, because indeed one shouldn't blindly optimize for typing, and I frequently find myself wondering when people tell me C++ is faster to write than Rust, when I (and others) empirically measured that completing a task, which is the interesting measure IMO, is twice as fast in the latter than in the former.
So I would have loved a defence of why more typing does not equate higher productivity. But this ain't it.
I have not read up on which tasks you're referring to that are empirically measured, apologies. The reason I'm curious on what the tasks are, is that depending on the task, navigability may not matter.
For example, if the task is "build a tool that does X", then navigability of the code does not matter. Once built, the tool does X, and there's no reason to revisit the code, and thus no reason to navigate the code.
But if the task is "Given a tool that already does W, X, Y, make the tool also do X', Y', and Z", then navigability of the code matters. This is because the coder must understand what the tool already does, and where the changes need to be made.
Most of my professional life, (and I'm willing to bet, most other coders here as well) I more often find myself in the second task than the first.
But, I'm not interested in Rust vs C++. I'd be more interested in the results of "given a version that makes high use of type inference vs not, how quickly can someone new to the project add X', Y', and Z." That would be a more appropriate test for what the author describes here. And I'd imagine that probably, those that are using sufficiently advanced IDEs would beat out those without, regardless of if type inference used or not, and would probably be slightly faster when given the highly type-inferenced version.