215 comments

[ 16.1 ms ] story [ 5734 ms ] thread
>I understand that <function exit> might be confusing for newcomers, and exiting might be too destructive. But denying you the normal REPL behavior and denying you the ability to exit is just insulting.

Sentence 1 provides a clear justification for the behavior. Then sentence 2 declares, by bare assertion, that it's "insulting".

I find sentence 1 more persuasive than sentence 2.

1 may be more persuasive but it’s 2 that makes me wish someone a very bad day every time that message prints.
The correct behavior is to print:

Are you sure you want to exit? [y]

and then you press enter again to exit.

The correct behavior from `repr()` isn't to do anything, including reading from stdin. That would break documentation generators, along with anything else that expects to be able to call `repr()` without side effects.
Nobody said anything about repr()? It's the REPL's job to handle this case and provide a good UX.
`exit` is simply a function. The text it displays at the REPL now is simply the result of calling `repr()` on that function. Adding a special case would make the REPL more complicated, and would make it inconsistent. Technically it wouldn't even be an REPL anymore, since it wouldn't be doing the "P" part for certain objects. And the special case would break tools that rely on the REPL behaving consistently.
First example seems odd? The author acknowledges that the two proposed alternatives (printing "<function exit>" or actually exiting) aren't great. The fact that the Python REPL provides a helpful hint seems like extra effort to make your life easier. This is especially notable given that in the next example, the author's complaint is about a command _not_ providing special guidance about the thing the user is probably trying to do.
Yeah. "I know it's not right, but it's not totally wrong and probably better than not doing anything" actions as maddening as the behaviour he describes. A world where that's the standard would be chaos.

When you work with _precise_ systems, they sometime take _precise_ input to work. That's kinda just part of the job.

Except in the case of the 'exit' clearly this is accepting and parsing imprecise input in order to guide the user. If the code wasn't looking for 'exit' in order to correct the user (to use 'exit()'), it would just spit out some other error for 'I don't know what you're talking about'. Instead, they actually detect the intent by specifically coding for it... and then ignore it anyway. That's bloody minded.
How do you feel about java/c++ compiler that give hints like "missing semicolon"? Clearly it knows what's wrong. Why not automagically fix that for you[1]?

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

JavaScript automatic semicolon insertion does a great job most of the time, but still leads to bafflement when code like

    const b = 1
    [1, 2, 3].forEach(console.log)
(from your linked page) does the wrong thing, because both forms (with and without a semicolon) are syntactically valid. I would much rather just always use semicolons (or, like Python, never use them) than have to memorise the 5% of oddball cases where I need to add them manually to resolve ambiguity.
I'd actually love that feature!

We have AI now that can write the entire code for you. Surely it's not much to ask that a compiler, as it parses your code and likely already has high confidence you need a semicolon in a certain spot, can just correct it for you and move on. Maybe have a --fix-errors option to the compiler command line or something.

Except they didn't -- the result of a statement is turned into a string, and the string is printed. There are standard ways of turning objects into strings, and the `__repr__` function on the `exit` object returns that string. If you call that object then it raises an exception that triggers a REPL to cleanly quit.

The code is here: https://github.com/python/cpython/blob/3.12/Lib/_sitebuiltin...

How about making `exit.__repr__` raise a CleanExit exception that gets caught in the outer REPL and then exits?
I'm fairly sure that having any object's `__repr__` throw an exception and exit would lead to even more confusion. Especially if it exits cleanly.

The object shouldn't be in scope anywhere other than the REPL, but that doesn't mean that something, somewhere, isn't stringifying everything because $REASONS and changing the behaviour won't cause an obscure "crash" somewhere unexpected and hard to debug.

If it actually exited, you might incorrectly conclude that exit would work the same way in a script.
> A world where that's the standard would be chaos.

We saw this play it with web browsers in the late 90s early ‘aughts where they’d do their best to render what they thought you meant if your html was wrong, and it was indeed chaos.

Strict systems with strict inputs makes for a better overall ecosystem.

I'd have done both the hint and the actual result. Like this:

    >>> print
    <built-in function print>
    >>> exit
    <built-in function exit>
    Hint: Use exit() or Ctrl-D (i.e. EOF) to exit
    >>> 

This way, it does do the thing you literally asked for, but also does help a newbie out.
> does help a newbie out

… or a long-time dev that switches languages fairly often :)

It should print the first to stdout and the second to stderr. That's completely consistent with how stdout and stderr are usually used.

The regular Python REPL doesn't distinguish between stdout and stderr (perhaps it should!), but you can embed it in things like Jupyter notebooks that do.

> It should print the first to stdout and the second to stderr. That's completely consistent with how stdout and stderr are usually used.

That makes sense from a unix tool perspective, but not from a python repl perspective. The repl's behavior is to print the result from the last executed statement. For the exit function, the __repr__ method was overwriten to print the message. That way when you type in "exit", the last value would be exit (the function), and the overwritten __repr__ method causes the help message to be printed. There's no way to have it print to both without adding in some repl specific hacks.

    >>> exit
    Use exit() or Ctrl-Z plus Return to exit
    >>> exit.__repr__()
    'Use exit() or Ctrl-Z plus Return to exit'
How about

    def __repr__(self):
        import warnings
        warnings.warn("Use exit() or Ctrl-Z plus Return to exit")
        return super().repr()
What if you aren't in the REPL? Using __repr__ at all seems like an abstraction violation. It should be the REPL's job to layer on special casing for exit.
You could use the same pattern as DeprecationWarning. It's suppressed by default and test runners like pytest enable it. A new ReplWarning could be enabled by interactive tools only.

I think I agree though: what you really want to special case is the situation where the user types precisely 'exit<cr>' at a REPL prompt, and that hack needs to exist further up the stack than in the implementation of __repr__.

The actual behavior is indeed a bit ridiculous:

    >>> print('%r' % str)
    <class 'str'>
    >>> def whatisit(x):
    ...     print('It is %r' % x)
    ... 
    >>> whatisit(min)
    It is <built-in function min>
    >>> whatisit(exit)
    It is Use exit() or Ctrl-D (i.e. EOF) to exit
Excuse me?

IMO if the REPL wanted a friendly feature like this, it should be a generic REPL feature, not a hack applied to the function exit.

Also a bit funny:

    >>> def get_function():
    ...     return exit
    ... 
    >>> a = get_function()
    >>> a
    Use exit() or Ctrl-D (i.e. EOF) to exit
Thanks for this, it never occurred to me it used __repr__ for the message. I just added this to my .pythonrc

    exit.__class__.__repr__ = lambda _: exit()
    quit = exit
Edit, you also need this in your bashrc:

    export PYTHONSTARTUP=~/.pythonrc
But really it should just exit. There's really no reason why it shouldn't.

I think telnet does something stupid along the same lines "I know you want to quit but please ask address me as Sir first".

There would be two ways to implement that, neither of which are particularly good. You could either have the exit function call itself from its __repr__ method, which an abstraction violation so egregious it introduces security vulnerabilities (imagine a logger printing repr(thing) to stderr, and someone sneaking the exit function in there for it to print), or you could special case it by making exit a reserved word, which flies in the face of the Python 3 ethos which changed print from a reserved word to a function and breaks a lot of established code.

Programming languages have rules that the programmer is expected to learn. Part of being a programmer is foregoing a certain amount of user friendliness in favor of an environment that is more powerful so that we can actually get things done. Programmers are paid to memorize and follow these rules so that the end user does not have to learn them.

You're trying to think of problems, not solutions.

It's pretty easy to think of a good solution with few enough downsides that overall the design is much better:

In the REPL only, if you type "exit" and press enter, it quits.

No changes to Python semantics required. All code still works. Much friendlier UX.

I wonder what dubious justification the Python Devs would come up with to avoid implementing that (and therefore admitting they've been wrong for 20 years). They'd probably try and claim it is more confusing to beginners or some nonsense like that.

Yes, I completely agree. If you want to teach the user the rules, then you follow the rules. The problem with the Python example is that they DID special-case it, but in an unhelpful way.
How is it unhelpful? It literally tells what to do.

Is it too hard to write exit() or press Ctrl+D after having incorrectly entered exit?

The special case is actually less a special case than you'd expect. It's "just" a python object that does some slightly funky things when turned into a string[0]. Making plain `exit` actually exit would mean triggering it accidentally could get too easy or would need a special case in the interpreter itself.

[0] https://github.com/Source-Python-Dev-Team/Source.Python/blob...

Vim iirc has a similar hint telling you to exit when you try something that should exit. I don't remember how it's triggered, ctrl-q maybe (edit, ctrl-c). I see the point, if you know what someone is trying to do, better to just do it, or ignore it, not give a condescending "nudge". Though personally the hint doesn't bother me.
Yeah, and it’s also useful to teach newbies about the usual syntax for doing things rather than introducing special-case magic. It would be _weird_ to just quit after writing a bare “exit”.
ipython intentionally diverges from the regular python REPL. It just quits like you expect without crying about it.
> The fact that the Python REPL provides a helpful hint seems like extra effort to make your life easier.

I agree; Python is actually helping you out here, since just typing `exit` doesn't actually call the callable.

Also, Python being Python, while not recommended, there's nothing stopping the user from assigning to `exit` then printing it in the repl:

    >>> exit = 42
    >>> exit
    42
What would the author expect Python to do in this instance?
I think the current behavior is worse simply exiting. The only concern with exiting when the user types "exit" is that perhaps the user actually wanted to see the value of the `exit` function. But the current behavior doesn't show the value of the `exit` function either, so it's useless for that use case.
Weirdly I'm very used to this kind of stuff and take it very easily from programmers, but when a german bureaucrat does this same thing to me I'm also fuming.
the trouble with "do what I mean" is that if the software guesses, and guesses wrong, the results are far worse than just not doing it. an "if you really meant to do this here is the correct incantation" error message seems like the best possible thing to do.
The problem with this argument is the low hanging fruit. EXIT is pretty unambiguous. I HATE that some tools the help argument is -? and others use --? when everyone COULD just print help on both is maddening, especially when you use the "wrong" one and it just says "do the other for help". You KNEW what I wanted, and didn't do it, just like the article says.

Yes, mindreading is tricky, but sometimes you don't need to read minds to just work with the user.

The problem is that all the "do what I mean" examples become part of the spec, and then have to be supported forever. Whereas "prints an extra-informative error" is much easier to change.

Even for low hanging fruit. eg, `exit` in Python can be redefined as a variable - so if I do that and then type `exit`, should it print that value for me or quit the REPL? Is it better to guess, or just not encourage the user to do the wrong thing?

If I make `-?` print help, what happens when I want to define `-` as "read the following text as input" and then users start getting bizarre behavior instead of the help file?

You can probably come up with ever-fancier ways to work around this, but that takes effort, and more testing, and more code...

> If I make `-?` print help, what happens when I want to define `-` as "read the following text as input" and then users start getting bizarre behavior instead of the help file?

I'm not sure what that means, but if you're saying that you would want your program to interpret "-?" as though somebody had run it with no option at all and then provided a question mark on the standard input, that's really obnoxious and grounds for torches and pitchforks from all users.

Look, on a command line, a bare hyphen, given as an argument where there might otherwise be a filename, can reasonably mean "read from the standard input" or "write to the standard output", but only because it's been so common for so long. As an alternative to it, you can just use standard input/output by default, or you can just punt and make the user give you something like "/dev/stdin".

In any other context, a hyphen introduces an option that modifies normal program behavior. If it's a single hyphen, then the option name is a single character. If it's a double hyphen, the option name is the rest of the argument up to any equal sign. It should consist of complete words and be in `kebab-case`, not `snake_case`. But even if you violate those rules, it's a far more basic rule that an argument starting with a hyphen is an OPTION NAME, not arbitrary data.

And the options "-h", "--help", and I guess maybe "-?" and "--?" are pretty much universally used to mean "print help about how to use your program". Changing that is insane.

I know that code on on Windows does horrific stuff (including pretending it's freaking RSX-11 and using slashes of all things). I know that some really old UNIX stuff written in the bad old days also breaks the rules. But even those don't just arbitrarily redefine what the hyphen means. Redefining absolutely everything under the user, to the point where they can't even guess how to ask for help, is just crazy.

Wait what? It would never occur to me to try to use a question mark as a command option at all. That's what "--help" is for. I'm actually surprised any program at all recognizes either form of the question mark.

... but don't get me started on people who think it's ok to use a single hyphen to start a multicharacter option, or don't let you put multiple single character options after the single hyphen...

Windows uses /? as the standard help flag, so maybe there are tools that are ported from that convention?

-h/--help are all I ever see in macOS CLI tools, in any case.

I believe /? started well before Windows, with DOS. IIRC, it was used because -? was common with Unix and CP/M (and DOS is basically half Unix and half CP/M) -- but Microsoft made the decision to have '/' be the switch character rather than '-'. So, /? was inherited from what came before, just slightly modified.
> It would never occur to me to try to use a question mark as a command option at all. That's what "--help" is for.

-? has been a common switch for decades. I've been implementing it (along with the synonyms -h, --help, etc.) in all of my programs since about the '90s.

Anything a user tries to do to ask for help is correct behavior that you need to support. It is a crime to tell the user “you asked for help incorrectly”.

I suggest supporting at least all of the following: -h, -help, --help, -?, /h, /?, help, <no flag> (if possible). Other flags don’t have to follow these rules, but I think help can be an exception.

And maybe you should print the help any time the user enters a bad command.

Just to be clear, I'm not saying programs shouldn't provide help in response to "`-?`". It sounds like a good idea to me, because that would be an obvious guess for what a user would mean by putting a question mark there. And, as you say, some help is a good response to any unrecognized option.

I'm just saying that I hadn't realized programs did that. And I've been around a long, long time. I guess I'm just blind.

> ... but don't get me started on people who think it's ok to use a single hyphen to start a multicharacter option

There are quite a few older programs that do that, including ImageMagick and FFmpeg.

I'm pretty sure slavery was also going on when those programs started doing that. Doing it now is kind of different...
i agree about the help flags, but not about `exit`, given that `exit()` is implemented as a function and not a repl builtin. python functions do not get run unless called with `()` and it's not worth introducing that inconsistency in there.
exit() is unambiguous, syntactically and semantically.

exit is unambiguously broken syntax, it doesn't fit into what the REPL promises, it's an error.

newbies to unix need to learn about Ctrl-D, and it should be taught. But it should not be the job of every tool to teach that lesson, it's one step removed from QWERTY. There are actually apps that break Ctrl-D and they are another abomination in the direction of newbie hand holding.

The worst is a few tools where when you type:

    the_command -foo
...and the tool responds with:

    -foo: No such option. You probably meant --foo
Well shit! Thank you for that burst of empathy. If you, developer, are that confident that's what I meant why couldn't you just make both inputs do the same thing? You went out of your way to make the code handle -foo, but only to have the program fire off that snide remark instead of doing what you know the user meant to do. Maddening.
Given the number of times I run

    git psuh
To be told "you probably mean push"... yeah it'd be easier for me if they went ahead and accepted the typo :D
Just add

    [alias]
        psuh = push
to your ~/.gitconfig?

I personally have "ull = pull" and "ush = push" in this section, together with "alias gitp='git'" in my ~/.bash_aliases because of how often I type "gitp ull".

I have "st = status" but you're absolutely right I should probably get around to updating!
Because you may have meant --f=oo or -f -o -o, both of which are standard unix interpretations of single-dash multi-character flags (e.g. ls, ssh). Or maybe the newest version added the -foo flag.

And if you've got something like 'sed' overwriting the input file (-i), it'd better make sure the flags are exactly as documented.

I feel this post. I can relate.

I once said - and still maintain - that all devs need a life-long lesson in empathy. For our users and our library consumers and so on.

It will be impossible to fix every conceivable issue and sometimes we will have to deprecate or break backward compatibility or features or whatever. But if maybe some more effort to understand and respect our users was made as a matter of course, then when we are users we will be more inclined to give the benefit of the doubt to the developer who created the thing.

Also the post made me laugh.

The last example is actually a great argument in favor of this “user-unfriendly” mindset. The removal of that CLI flag forced the user to actually figure out the root cause of the problem rather than paper it over with a workaround they didn’t really understand, which would inevitably metastasize and explode later in an even more frustrating way.

All of these examples are devs trying to make sure users understand what’s going on. Nothing wrong with that when it is necessary complexity. The trouble I think comes when you’re making sure the user understands something that really shouldn’t matter - unnecessary complexity. Especially complexity that is just a byproduct of a disorganized group of people building a “bazaar”, like in the Node ecosystem (and apparently Rust too). Who cares if X is actually a fork of Y that was merged into Z.

> The removal of that CLI flag forced the user to actually figure out the root cause of the problem rather than paper it over with a workaround they didn’t really understand, which would inevitably metastasize and explode later in an even more frustrating way.

I wish the author had written what the real issue is. It feels like they are contributing the the very problems they are complaining about with a flippant line about it being something else.

The problem was a strange monorepo setup where multiple packages came from the same GitHub repo. Then I tried to switch one of them to my fork. The error message did not help me understand this reality at all; I only figured it out the very hard way after blowing up my project in two different ways.
(comment deleted)
Such a weird rant. Yes, some tools do some things, but not others. Making tools do everything for everyone just bloats them to unmaintainable messes. And it’s a special kind of entitlement to believe that the way _I_ imagine things must be working is the only possible correct interpretation.

Reality is messy, there are trade-offs, and a lot of smart people think very hard about what the correct trade-offs should be given a particular situation.

And, the last pet peeve of mine. If I got a dime every time someone says ‘why don’t you just’ without even beginning to start to realize the depth of an issue, I’d be a very rich man. That’s about this quote here:

> What would it possibly take to “stabilize” such a feature? It is a completely trivial feature!

Not having a feature is fine. Kind of having a feature but hiding it behind obnoxious gatekeeping is not fine. Features that are buggy or inexplicably complex is not fine.

Some open source projects get this right, and others don't. Some projects don't care about wasting the user's time. Or they berate the user for wanting to do something in the 'wrong' way. Or they break things that worked just fine in the pursuit of some abstract purity.

It's not about complexity or technology at all. It's an attitude problem.

I really appreciate the way Rust feature gates unstable features. It's easy to opt into nightly if you need them, it's nice that new features are iterated until they are correct and it is nice to rely on stable being stable.
You can just prefix features with --experimental so people can use their own judgement. Software should warn users when they do something that might harm them. But it shouldn't patronizingly refuse to run.

"I'm sorry bryanlarsen, I'm afraid I can't do that." - 2023 a rustfmt Odyssey.

Is the code actually there on non-nightly builds or is it just a simple text response for the experimental flag?

For the former then sure, it should just let you do the thing and simply fail loudly if it fails. For the latter, there's not much they can really do without switching you over to nightly rust.

>Features that are buggy or inexplicably complex is not fine.

really depends on so many factors that this isn't a good general sentiment. Sure, if this is a professional grade tool targeting corporate, it better be polished or have excellent support for when stuff breaks. If it's some small scrappy tool handling a complex task with all kinds of edge cases I can give some leeway to the UX and even some very esoteric use cases.

vector graphics comes immediately to mind. I won't be too griped if Krita as a FOSS has some subtle bugs. I can at the very lest throw out an issue to at least bring awareness. I'd be much more miffed if Illustrator had some issue given how closed up and hard to contact Adobe is if you're not a million dollar business (not to mention Adobe's general philosophy of how they approach software these days).

Attitude is a factor, but I do take into account the problem space when evaluating feature completeness.

Some lie about working features to get people to read into the project and fix it for them. One liar and his flyer +42 fools make a open source Projekt, even if you can not code to save your life. These projects mostly consist of issues and pull requests..
> Reality is messy, there are trade-offs, and a lot of smart people think very hard about what the correct trade-offs should be given a particular situation.

This is great and all, but misses the point. If you have made a decision to cut a feature then at least explain why. This then circumvents the 'why don’t you just' mentality. That is the root of the issue in the examples provided. It would be trivial to drop a warning about the comments not being formatted and include a link to the issue. Then there is no question of 'why' and thus you're now heading off the 'It is a completely trivial feature!' attitude, and blog posts complaining about it. Communicate clearly.

The very first example in the article discredits efforts to add clear communication.
TBF the Rust comments did address the "why don't you" issues. The problem was that the alternatives were also outdated in and of themselves. It sounds more like a docs issue than a communication issue.
So what you're saying is that it was still a communication issue. I'd view it that way if what I was told was either wrong or very out of date. Docs are how you communicate.
Communication on one end. I don't agree with the dev comments, but I didn't see anything off putting about thr way the CLI comments were phrased. Most of the author's post seemed to be about the smug attitude (so, the former).
Writing code should be part of the OS as fundamentally as web browsing. You should get a cursor to write code, have a tree worth of files and have a button to publish the application as a single clickable program.

Anything else is a tribute to the rant.

Just like there was only one OS out there…

But I could see the the madness anytime I squint my eyes, a large part of computing is just this.

It’s a miracle that it works at all..

Ah yes, and when I write a new language all os vendors should be forced to integrate it, or I should be blocked from inventing a new language. None of this installing code that others wrote. If I want to write a language my only option should be writing an os from the kernel up.
Give it the appropriate file extension and the os downloads relevant programs and makes it work.
I agree with the rant, but not the examples.

It pisses me off when programmers add "features" which forces users to deal with them.

For example, pipenv always prints a message when you activate an environment (I don't know if it's still the case because I don't use it anymore). I had to migrate a cronjob which ran a Python script and sent an error email if anything got printed to stderr.

This is a pretty standard Unix thing to do, but pipenv decided to always print to stderr that the environment got activated, even though it's not an error. And there is of course no way to get it to shut up. I had to use some Bash hackery to filter that out.

Ryan Dahl's 2011 rant on this is quite relevant:

> Those of you who still find it enjoyable to learn the details of, say, a programming language - being able to happily recite off if NaN equals or does not equal null - you just don’t yet understand how utterly fucked the whole thing is. If you think it would be cute to align all of the equals signs in your code, if you spend time configuring your window manager or editor, if put unicode check marks in your test runner, if you add unnecessary hierarchies in your code directories, if you are doing anything beyond just solving the problem - you don’t understand how fucked the whole thing is. No one gives a fuck about the glib object model.

> pipenv decided to always print to stderr that the environment got activated, even though it's not an error.

This is a pet peeve of mine. I see it every so often and every time, I wonder if the dev wasn't familiar with the norms of the platform or if the dev was being malicious.

Either way, it doesn't speak well for them.

>if you are doing anything beyond just solving the problem

It's a rant so I won't take it TOO literally, but: The opposite extreme of this philosophy is why we have stuff like Blender pre-2.7 and GIMP. There is some use to polishing up a proper looking UI and ensuring familiar UX. Especially if your audience is non-developers.

But yes, I understand. configs and CLIs should be as "dumb" as possible. Proper line breaks for readability and maybe color if needed, but there's nothing more frustrating than debugging what comes down to a bare basic command.

stderr is not reserved for errors. It is perfectly acceptable in *nix to print diagnostics and logs to stderr. Granted, pipenv should have a quiet flag, but you should be checking the status code if you want to be notified of errors.
I think the point is not whether certain tool does things in certain way. It's the lack of thought about how the user interacts with the tool and whether or not the particular way of doing it makes the interaction easier. Too often "technically correct" is considered the best kind of correct even though the resulting interface is infuriatingly unpleasant to use. It's hard to please everyone, but it is useful to remind people to at least try to be more thoughtful about this aspect.
Well, but the cases he mentions are really not “just not being thoughtful”.

I’m old enough to remember that Python 2 in fact had “print <string>”, and I think also “exit”. Exactly because once upon a time Guido thought about what would feel nice to users. In time it turned out that special casing a handful of special functions introduces pain across everywhere else. Because of its valid syntax in REPL, it’s valid Python. So they removed the special casing for these functions, and added a helpful comment for people who might be confused.

Ditto for wrapping comments in Rust. Doc strings in Rust can have doc-tests in them. Meaning that whatever comment wrapping you do must not break syntax there. I don’t know what feature exactly is needed there that’s currently nightly only, but I’m very sure it’s not just something trivial that nobody has gotten around to yet.

Point being, no, everyone else except _me_ is not some dumb f*ck, who doesn’t do the obviously right thing because they hate me.

Like, that _sometimes_ happens. But these are not those cases, and in my experience it actually happens pretty rarely. I think the default disposition towards seemingly stupid implementations or behaviors should be that people working on the implementation were not sloppy idiots who hate their users, but rather that they had their reasons, and me being put into their shoes would’ve likely done the same.

> So they removed the special casing for these functions, and added a helpful comment for people who might be confused.

Technically correct. But: what happens is that when you start python, it outputs:

Type "help", "copyright", "credits" or "license" for more information.

But when you type "license", you get this:

Type license() to see the full license text

Why that happens? Because "license" is a special object that has string representation as presented above and to actually read the license, you need to call it and not just request string representation. Technically it's all clean and correct. But guess what - the user does not care. Your design is not the user's problem, the usability is the user's problem. And the usability of this kind of solution is terrible. I know why it's done, I know how it's done, I can appreciate the technical beauty of it - and it still pisses me off. They clearly knew about this case, they themselves told me to use "license" and when I did it, they say "nah bro, you forgot the parens, you idiot, try again, har har har!" It's not a huge deal, but when it happens to you thousands of times, it eventually starts to piss you off. Avoiding this kind of thing is not easy - it may actually require departing from the nice and clean design you envisioned internally, and that may be annoying to a programmer - but as a user, I much prefer that this is how it would be done. I know there are reasons, in many cases I know exactly what the reasons are, because I've been guilty myself, but that doesn't help - the whole point is, as a user, it makes my life harder. So, in our development as a software engineers, once we learn how to build technically correct things, we also need to start thinking past that - how to build things that people would enjoy using.

The promise of.. You don't need to be a domain expert but to configure and use it you do. Needs a expert and a autoconfig noob api.
> “Unstable features”???? How is wrapping text an unstable feature in a code formatter?

Haven't finished the whole thing, but wait till you start reading about LaTeX.

The unstable warning preventing you from wrapping the comments actually seems pretty reasonable to me.

It is implemented, but there are probably edge cases where the proper way to wrap and retain the best possible formatting is still disputed. Since a lot of people format on save, via commit hooks or during CI, having this feature enabled as stable and _changing the behavior later_ would cause massive annoying diffs in the future, or CI failures for a ton of projects.

other posts pointed out the issue, it breaks certain markdown formatted comments (mainly tables).
It would be very valuable if that was part of the tool's explanation then.
Gating it behind nightly is just bizarre though. Today's nightly, where the feature is available, will turn into 1.76 or whatever, but then the feature will become unavailable again. That is not how normal software works.
(comment deleted)
I don't know the intention of the Rust team, but maybe it's because of the difference in release cycles: if a bug is found in an unstable feature today, it can be fixed by tomorrow's nightly build, but it won't make it into stable until at least February. Presumably, the maintainers don't want to release code that is in flux and under active development under the same 6-week release cycle that they release stable, well-tested code.
Not all patches will be automatically pulled into the next stable release. Features can stay in nightly for a long time while their bugs are getting ironed out.
I like the MATLAB REPL a lot with regards to this issue. If you make a typo in a function or variable name, the REPL fixes it automatically and you press enter to send the corrected command. From the story submitted here about Medley Interlisp, I gather that its REPL also has such a feature.
Do what I mean, not what I said can be problematic for tools. That being said, the example where the devs left a link to file an issue to beg to restore functionality was maddening.
"Beg", and "passive aggressive" from TFA, is an unnecessarily emotional interpretation of that sentence. It's perfectly neutral. When they imported `cargo-vendor` into cargo they removed a feature that was not trivial to reimplement, so they asked for an issue to be opened so that they can see if people want it and so that someone can decide to implement it.

That message *could* be updated to point to https://github.com/rust-lang/cargo/issues/10310 instead of asking for new issues to be created or suggesting the old `cargo-vendor`. (The author of TFA already knows about that issue, since they commented on it before they published their article.)

(You might say it would've been better to let cargo-vendor remain separate instead of merging it into cargo, but the reason that was done was to ensure it would continue to work with changes to cargo. Indeed that is why the original cargo-vendor does *not* work properly any more.)

Agreed, but in that case the tool shouldn't be trying to detect what I mean. Why look for 'exit' only so you can print 'no actually, it's exit() dummy'. If you're parsing for it then just bloody do it! Otherwise I'll get a syntax error and realise that 'exit' isn't the right way to exit. And I'll do a quick search myself. But explicitly looking for and detecting my intention and then ignoring it is just dumb.
Nothing is looking for exit. The REPL is calling repr(result) to show the result of the expression to the user. repr(exit) is the string "Use exit() or Ctrl-D (i.e. EOF) to exit". You _really_ don’t want repr(builtin) to have a side-effect like terminating the program.

Stop being toxic and patronizing towards open-source developers out of ignorance.

ipython has specifically implemented an Autocall that allows you to exit by typing exit, quit, exit() or quit().

https://ipython.readthedocs.io/en/stable/api/generated/IPyth...

And repr(exit) also works fine:

> In [1]: repr(exit)

> Out[1]: '<IPython.core.autocall.ExitAutocall object at 0x102772770>'

Maybe don't be so eager to accuse people of ignorance and toxicity.

Perhaps a variable `exit = True` or something was defined previously, you don't want to have the language or REPL handle a myriad of possible corner cases, that would be very brittle. But a printing warning or lint-like suggestion is ok and actually useful.
It's not brittle. ipython has a special case for exit and quit. See my sibling comment. If you try to assign to exit you get a syntax error. Which is weird, but also fine in practice.
It's so strange to me that the first example is one where the devs went out of their way to help the users. There's no world in which that's maliciousness or anything of the sort.
>It wasn’t seeing my file, despite it absolutely being part of the project. It even found the lib.rs in my crate but not this particular source file. I have no idea why,

Because it's not in fact part of their project, despite their assertion that it is.

>But you can’t. cargo fmt doesn’t have an option for that. How is even possible for your format command to not let you format an individual file?

Because cargo is a tool for working with projects, not with individual source files.

>Problem is, I had never even heard of rustfmt.

Putting "rust how to format a file" into a search engine returns rustfmt in the top results. But in any case, the problem they needed to fix is to make that file a part of their project so that `cargo fmt` will format it, not work around that by running rustfmt manually. But alas they've convinced themselves that's not the problem.

>At this point I was just hopping mad. It knew that I wanted to wrap comments. But it refused, because this was “unstable”. It knew that I wanted to opt into experimental features, but refused again.

Whatever method they used to install nightly, they're not using it here. The error is coming from the non-nightly rustfmt that they were using originally.

>It would literally have been better if they had not shipped this wrap_comments feature at all. Instead they just keep dangling it in front of my face and snatching it away as soon as I try to use it. Because they hate me.

I'd be amused at this trolling if I didn't know the author is completely serious.

Some of us just wander into the Rust part of the codebase every so often for our work.

I cd’d into the folder with the cargo.toml, ran cargo fmt, and nothing happened. I dunno what else to tell you. Sometimes things just aren’t set up the “right way”, and yet we still have to do our jobs.

Does cargo test run unit tests in the file you're expecting to be formatted that isn't?
As a software developer, I am in wonder at the shape some software ships in. The last couple of days, I have toyed with Raspian bookworm together with my father.

Its menu contains an 'Add-Remove Software' GUI tool, which seems to be a thin and ill-conceived wrapper around apt-get (?). It combines several horrible choices, which conflict and clash perfectly. (1) it appears to insist on retrieving and building its entire list of apt packages, EVERY TIME YOU TRY TO OPEN OR OPERATE IT. (2) it doesn't give you any choice to CACHE this data, or any choice to skip this "refresh". (3) it takes five minutes or more to refresh this list, on our raspberry 3. (4) on any excuse you give it, it will refresh the list again. (5) once it succeeds in retrieving this insane 5-minute list, it presents it to you in an unnavigable manner - no sorting, no searching, no filtering, just a scrolling list with something like 10.000 apt packages.. page up/page down doesn't seem to work. For example, I naively pressed the cursor keys, wrongly assuming it would move my selection point in the 10.000+ list.. WRONG: Instead, it navigated a category-word-list, causing it to redo the 5-minute re-init. In effect, it because a 'trial and error game from hell', where you can attempt 10 wrong ways to navigate the list, each time being punished with a 5-minute "no you can no longer view the list you just waited 5 minutes to retrieve, you must now wait 5 minutes again for your next wrong attempt!" (6) oh yeah, and you are not allowed to view the partial list during load. I really really wonder who decided to implement that window in that maddening way??

In the end, I resorted to using apt search in the command line, which of course works perfectly, but not before having wasted about an hour together with my less-tech-savvy dad, trying to make the raspian package install UI "work" in the naive idea it would be easier for him to use :-/.

Certain Linux UI stuff can be an opinionated death by a thousand papercuts. Probably because most Linux devs don't dogfood their own stuff as they just stick to the command line anyway, so there's less incentive to fix the UI issues, if nobody in the dev chain is actually using it.

I remeber when Linux GUI users were asking for a "Right click -> run/open as root" feature just like in Windows, and the devs basically said something along the lines of "we're not gonna do it just because the way Windows does it means it must be wrong; if you need to run stuff as root it means you're a poweruser so you should use the comand line anyway; you're welcome". Linux Mint was the first and only to have this feature in the GUI early on, then came the other DEs who ceded defeat to sanity.

(comment deleted)
> I really really wonder who decided to implement that window in that maddening way??

They implemented it in a test environment with either an emulated device pulling packages from the host, or pulling packages over a gigabit network from a local cache on their LAN, or on a high-speed fiber connection where it took far less than 5 minutes to retrieve the list.

Empathy is hard when dealing with thousands of users.

For everything you think should obviously be A there are many vocal people who think B...

interaction design for API and CLI, text based interfaces, is both hard and underdeveloped.

for example argparse and similar cli argument parsers _know_ the basic structure of the command line, but they don't provide it in machine table form, to interactive shells, so these could ask CLI tools: "hey, what do you expect at this point?"

This assumes 100% competent and knowledgeable users. Nobody is in every field, and so the question is "do we freely hand out footguns, or should we put a safety in place"

In some fields, the decision is easy. We're by now culturally aligned that shipping encryption libs with bad APIs or a default-on footgun option is a bad idea.

Clearly, with less impactful tools, we still have more debate ahead of us. I'm hoping that we'll realize at some point that proper engineering is always fail-closed[1] if there are destructive consequences. Yes, it might temporarily inconvenience some, but it prevents harm to more.

Ultimately, the question is "Who is more important, you, or the rest of the world".

[1] fail-open if you're EE. We're so bad, we can't even decide on terminology.

Writing this sort of rant in English is meta-hilarious.
The author provides very surface-level criticism of two Rust tools , but they don't look into why those choices were made. I'm ignoring the Python complaint since it's discussed in other comments.

With about five minutes of my time, I found out:

wrap_comments was introduced in 2019 [0]. There are bugs in the implementation (it breaks Markdown tables), so the option hasn't been marked as stable. Progress on the issue has been spotty.

--no-merge-sources is not trivial to re-implement [1]. The author has already explained why the flag no longer works -- Cargo integrated the command, but not all of the flags. This commit [2] explains why this functionality was removed in the first place.

Rust is open source, so the author of this blog post could improve the state of the software they care about by championing these issues. The --no-merge-sources error message even encourages you to open an issue, presumably so that the authors of Cargo can gauge the importance of certain flags/features.

You could even do something much simpler, like adding a comment to the related issues mentioning that you ran into these rough edges and that it made your life a little worse, or with a workaround that you found.

Alternatively, you can continue to write about how much free software sucks.

[0]: https://github.com/rust-lang/rustfmt/issues/3347

[1]: https://github.com/rust-lang/cargo/pull/10344

[2]: https://github.com/rust-lang/cargo/commit/3842d8e6f20067f716...

5 years to get a wrap_comments feature stable? I sometime wonder if people read the things they write?
I mean... check out the issue tracker for any project, open source or commercial. It's just how it goes when things aren't a priority. Happens all the time.
The conversation was about why the feature wasn't stable.

I have no idea how the Rust project organizes itself, but my guess is that this simply isn't a high enough priority for them, which would explain why it hasn't been made stable.

Large backlogs with stale are a common problem that software projects have.

I don't know if this is hard in their codebase (although I have a guess), but I have worked on a code formatter before and easy-sounding things were often an unimaginable nightmare.
Code reformatting is, apparently, surprisingly hard. This classic article has popped up on HN a few times over the years: https://journal.stuffwithstuff.com/2015/09/08/the-hardest-pr... (one reappearance at https://news.ycombinator.com/item?id=30566111)

So I can understand why such a feature remains behind an experimental flag. "But you are not touching code, only reformatting comments!" - well, not just thta. Adding whitespace and N+1 effectively empty lines will at some point cascade into whatever logic the wider code formatter will have to consider. The number of edge cases must be non-trivial.

> you can continue to write about how much free software sucks.

I think that's unfair. He wasn't writing about how much free software sucks, he was writing about how some of the rust tooling sucks.

Even in the larger context, everything he wrote applies just as readily to proprietary software. These sorts of issues are everywhere.

The post itself isn't very generous; its title is “You can’t do that because I hate you.”

You're right that "frustrating to use software" applies to proprietary software as well, but the author only gives very specific examples all of which involve open source.

> The post itself isn't very generous; its title is “You can’t do that because I hate you.”

Well, sure, the post is a good old-fashioned rant to vent his frustrations. The title is part of that. A well-written one, but nothing more than a rant.

You're right.

Rants are fine, and I've been frustrated by software too many times to count.

I'm being a bit protective because I would feel hurt if someone wrote that "I hate them" about some of the software I've worked on for free and open sourced.

I feel the same way about Rust unstable features. Either you can only use features that are "stabilized"—guaranteed to never change—or you can throw all guarantees out the window, and use the unstable, experimental version of the toolchain.

This would be fine if "unstable" features were actually experimental messes, but all sorts of completely-benign utility functions are "unstable". You have to throw out all guarantees for your entire toolchain just to use, say, `Result::into_ok`, which was introduced in 2019, but is still unstable due to being "newly added".

At best, this is misleading, at worst, this greatly compromises the developer experience, especially when writing libraries which need to work on stable or else nobody will install them. Even for application projects, I don't want to use unstable features, because it's extraordinarily stupid to make the entire project nightly-only just because I want to use a two-line utility function, and I'm petty that way.

Sure, I'll use unstable for things like bare-metal programming that deserve to be unstable, but if I'm writing regular application code and the perfect utility function is "unstable", I'm not going to lock myself into nightly just for that.

> but is still unstable due to being "newly added".

Where did you get this from? The tracking issue says nothing of the sort: https://github.com/rust-lang/rust/issues/61695

Right, it's nothing to do with it being "newly added".

That issue had been filed by its OP as a feature request. It then got repurposed into a tracking issue for the feature, but it's not following the standard tracking issue template that lists any blockers, stabilization steps, etc. (Compare to https://github.com/rust-lang/rust/issues/119364 that happens to be the newest "Tracking issue" atm.) Also since the OP didn't intend for it to be the tracking issue, it's possible they're not intending to do the rest of the bureaucracy to get it stabilized either, which is why it's stalled.

> Right, it's nothing to do with it being "newly added".

It absolutely was, but now it seems to be mostly that the stabilization process is tedious enough that nobody has done it yet for this tiny utility function. There are tens of other tiny utility functions in the same situation that have been sitting for years, I run into them all the time.

The whole situation surrounding "unstable features" is just super disappointing for me. This stuff doesn't deserve to be unstable, and I shouldn't have to switch to an experimental toolchain that breaks all the time just to use these tiny utility functions!

>It absolutely was

No, there is no such absolute requirement that "newly added" functions cannot be stabilized. Off the top of my head: https://github.com/rust-lang/rust/issues/111544 `OsStr::as_encoded_bytes` was proposed in 2023-05, finished being implemented in 2023-07, stabilized in 2023-09, and released in 2023-11.

>https://news.ycombinator.com/item?id=38801228

The reason attr of "newly added" is the reason to *mark the function unstable* when it was implemented. It's not the reason to *prevent it from being stabilized.*

> Where did you get this from?

The source code?

    #[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")]
And that issue you linked is from 2019 and full of people asking when it will be stabilized. Last activity was in late 2021.
That annotation hasn't been touched (aside from being moved around) since it was authored [0].

Your comment said:

> `Result::into_ok`, which was introduced in 2019, but is still unstable due to being "newly added".

Your comment makes it sound that the Rust team considers a four year old utility method as "newly added", and that's the reason why this method is marked as unstable. More likely, this "newly added" reason is something they add for any new unstable feature, and it is now out of date.

Considering that repository has 9,000 open issues, and 50,0000 total issues, you can imagine that this issue has simply not been prioritized. Rust being poor at prioritizing, handling issues, etc. is a totally separate discussion from "Rust thinks four year old code is new"

[0]: https://github.com/rust-lang/rust/commit/c784720f3a2d0b66142...

There's nothing wrong with their prioritization, there are tons and tons of high-priority issues that deserve more contributor time than this. Everyone's a volunteer.

This is just a process error, IMHO. The process for stabilization in general just results in outliers like this and it's really frustrating for everyone involved. I know Rust is sort of unique here, just like it's unique in all sorts of other ways, but I can't help but think that there could have been some sort of provision for small methods like these.

Rust is a great language but the developers suffer really badly from Reddit Intellectual syndrome. If they put some people with social skills at the forefront of relations with users it would definitely get more adoption.
Rust and TypeScript came onto the scene at about the same time. Microsoft manages TS more or less, Redditeurs manage Rust.

Fun to see which is thriving and which is still niche!

(comment deleted)
It’s a hard job that requires a lot of learning, failing, adapting, and growing. It’s frustrating, but getting good can only be done through endless grind. There are no cheat codes. After encountering and overcoming the roadblocks mentioned in the article, you grow keen to that class of problem, and next time, more quickly figure it out and move on.

An apprentice woodworker is going to fuck up a cut and have to spend hours rebuilding. An aspiring musician is going to pour hours into mediocre music that nobody listens to. A new software engineer is going to get lost in the sea of complexity built on top of the rapid growth and death of fads and doctrines and tools (good and bad) that is decades deep.

It’s fucking hard. I do think the pay reflects that, though perhaps is a biiiiit inflated. Regardless, shit ain’t easy. That being said, people generally quite enjoy sharing their knowledge, and helping the next generation of software people ramp up and kick ass, so there may be hope yet :)

I empathize with a lot of this. With experience and knowledge it's easy to recognize why and how these things happen, to point them out, and to call the author wrong. But I also remember being new to tools and not understanding how arbitrary they seem to be, in that they can understand what I want to do with uncanny precision but simultaneously refuse to do it. Things like exiting vim are the gold standard of this.[1]

But:

> Either support the feature, guide the user toward a better solution, or do nothing.

The first two examples _explicitly_ guide the user toward better solutions. That's much more than many tools do. If the Python repl behavior isn't "guide the user toward a better solution" in both letter and spirit, I'd love to know why.

There are much lower-hanging fruit than these examples, which makes a good point harder to agree with.

1: One of the first-page Google results for me on quitting vim is somehow this HN comment thread from 2017, in which the Python repl behavior catches strays with almost identical arguments as this article. https://news.ycombinator.com/item?id=14403297

>Exiting Python

In early versions of COBOL exit was frequently used to go out of loops and out of sections and so on. Maybe a message is nice for someone still trying that while learning modern stuff, but I admit it’s farfetched/unlikely.