This is something that seems to be settling down in language design. Local variables are usually implicitly typed, and function parameters are usually explicitly typed. This is for readability. "auto" was retrofitted to C/C++ so those languages have implicit typing of locals. C# and Java added "var". Rust uses "let". So this is now pretty standard.
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.
That's helpful, but not entirely true. The old meaning remains in C23 for explicitly specified types, and the `auto` of C23 and C++11 have some notable differences: https://dev.to/pauljlucas/auto-in-c23-ij9
I believe 'var' was introduced in C# to make it much easier to work with LINQ, where you can easily return a subset of properties - without var you would have had to declare interfaces. I think ReSharper is responsible for the rapid proliferation of using var everywhere.
Is ReSharper that prevalent? I've always thought it a small subset of programmer's who use it but are very vocal (and preachy). I've only seen it used twice 'in the wild'. And one of those two times most of the devs hated it because it was a performance hog, it was a preachy 'architect' shoving it down their throats.
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.
I think a better argument here is that we can't always assume code will be read in an IDE/code editor at all. There is value in being able to quickly glance at what some code is doing in your browser without downloading it from a repo, or in a preview view offered by a GUI file browser, or any number of lowest common denominator scenarios.
anyone with no time to properly setup an IDE before delving into some code to quickly find something out becomes that "vim/emacs user". Certainly in C++ you can't make sense of the code without the right defines and include path and configuring the IDE to actually understand C++ code is not that quick at times (and that's before we get into things like Qt which essentially add their own language features)
> This post can definitely be considered a “religious” opinion piece
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 presented has problems, but they are not the problems the author thinks it is.
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.
The article starts with a correct problem statement and concludes with a weird claim, because nowadays IDEs like Visual Studio Code do support the best of both worlds: inlay hints. I turn them off by default on VSCode (because it messes with columns, unfortunately) but configured them to `offUnlessPressed` so that I can quickly check inlay hints by pressing Ctrl+Alt/Option.
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.
Where do you draw the line between what the IDE is able to show and what is saved in the file? We could remove all indentation and spaces around all operators/symbols and newlines let the IDE inlay that too.
Yes, please? I can't wait until we stop saving code as text. It's silly. Kill the formatting, let everyone see the level they prefer, stop limiting everyone to file-level granularity. Show the types that are relevant in context and allow switching the views.
Rust and Kotlin would like a word. What a weird take. All editors now have LSP support now and your editor/compiler should definitely figure this out for you.
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.
Rust and Kotlin have much stricter type systems and much saner standard libraries. C++ auto is a ticket to hell of implicit
conversions and stupid extra copies that come out of nowhere.
Although there are inline hints, I agree with you that auto makes readability worse. Especially in situations like PRs and Git forges where inline hints are not always available.
The team trumps anything of course, because they can work around anything if they decide to.
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...
Python is so much worse with it's runtime dynamism though.
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.
Using one letter variables and the such is excusable if you are new to programming and don't know better or if you use it in such a local context that the short length helps readability or if it is a "known" short variable like i or some sort of speaking short variable like db.
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.
I'm okay with x, but only x. Naming things can be hard so you get one freebie, but only one, so you better figure out a proper name for everything else after you use x.
To add to the original article's point, even the IDEs are optimized for typing. Think how they auto complete { and " and other stuff that comes in pairs for you. Great when you crank out new code, can be maddening when you're editing.
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.
With the complexity and the performance cost considered, I don't think there is a piece of code that is worth writing in C++ that also don't need to care about types.
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.
Optimizing for navigation is an interesting notion. Lack of navigation is also my main problem with class hierarchies. Importing a class is like import *: you do not see anymore in the module itself where some method is defined.
It's for job security. Your replacement won't be able to figure out anything, will be very slow, get fired, and then they'll rehire you at consulting rates.
If the replacement can't figure out how to use an IDE that gives them the answer, they're very likely going to be slow - but auto has nothing to do with it. They're just not an experienced dev.
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());
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.
> empirically measured that completing a task... is twice as fast in [Rust] than in [C++]
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.
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.