Hacker News new | past | comments | ask | show | jobs | submit login
The human typewriter, or why optimizing for typing is short-sighted (felixk15.github.io)
38 points by ingve 6 months ago | hide | past | favorite | 56 comments



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.


C already had auto, for automatic storage class specification. It's the default anyway, so I've never seen it used in the wild.


C++ had that too, in 1998. C++11 and C23 changed the meaning of auto to mean "infer this type".


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


It's not settling down, as C# just changed it up again.

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 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.


> auto lovers will say that the IDE will resolve the types for you if you really care, but would someone please think of our poor Vim & Emacs users?

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.


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.


If LSP can tell you the type, wouldn't it be a nice solution to just have it write it in for you?


Things like that exist in neovim:)

but I'm curious if LSP can write it for me why not just have compiler infer it?


So you can figure out the type of a variable at a glance as you read the code?


If it's not so obvious that it requires to be stated then it has to be selected from a list suggested by IDE and critically inspected for correctness

Do you find it faster to context switch to that mode from typing flow? I don't always


Maybe you're not going to be the only one reading the code.


Code is read more often than written but dynamically typed languages exist:)


Maybe the poor Vim & Emacs users should think about other developers that have better things to do than catering to their special needs.


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.


> inlay hints

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 wish clangd let me configure the inlay hint elision length...


Yeah, I hope I didn't annoy anyone using editors that have IDE supports via LSP :-p


Well, you never know if vim users are upset because you are calling their editor an IDE or because you are calling it "not an IDE".

I'd say "vim user" is already an insult in itself, so that doesn't matter at all ;)


> 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"


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.


Also, any kind of weakly typed language.

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.

LLVM has a well thought out stance on auto too: https://llvm.org/docs/CodingStandards.html#id29


It's funny reading this coming from Python where I code without types and it's perfectly fine.

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


> 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???


You get that in the compiler error messages when you forget a ; 10 lines above ... best of both worlds ...


> where I code without types and it's perfectly fine

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.


I think you are missing the root idea here:

- 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.


But... by the time I get to run the script I've already written the code so I have no more need to know what that auto signified.

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 :)


a preprocessor program is how an IDE would do that under the hood and is accessible in general too.


I worked on a ~350kloc (dry) code base for 10 years. It was perfectly fine.

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.


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...


You mean different people surely? If it's the same people, the lack of types should be less of a problem.


The author uses a code snippet that looks like the average Python code:

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.


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.


While it impacts both, In wonder if--in the long run--there's more value from autocomplete in reducing error, as opposed to saving seconds of time.


The rules are simple:

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.


> but would someone please think of our poor Vim & Emacs users?

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.


My take is that auto primary advantage isn't optimizing for typing, it's optimizing for refactoring.


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.


Very disappointing read.

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.


> 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.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: