News for hackers. Stuff that matters.
Re:Dark side of ergonomics in Rust by panic on Saturday April 14, 2018 @05:50AM (#16836655)
Say you have some code like,

    auto button = Button::create();
    window.addSubview(button);
    button.setLabel("test");
    button.setAlignment(Alignment::TOP_LEFT);
    button.sizeToFit();
If setLabel() throws an exception, you'll end up with a half-initialized button in the view hierarchy.

For exceptions to work predictably, all mutation within a try-catch block should be captured in a transaction that can be rolled back. Then you don't need to worry about each function possibly throwing an exception. Either the entire block commits its changes, or it's as if the entire block didn't happen at all.

Re:Dark side of ergonomics in Rust by ben0x539 on Saturday April 14, 2018 @09:54AM (#16837317)
Wouldn't this be fixed simply by calling `addSubview` last (and designing your UI toolkit in such a way that you don't need to imperatively call `sizeToFit` after adding a thing to a container, but implicitly sizing things correctly during layout)?
Re:Dark side of ergonomics in Rust by camgunz on Saturday April 14, 2018 @11:53AM (#16837834)
Sometimes whatever `window` and `button` actually are have references to each other (ex: parent/children relationships) so it's possible -- in fact it's surprisingly common that -- there is no correct order.

You need total knowledge of what can throw exceptions, and while the same is true for status codes, the raison d'etre for exceptions is shrinking the amount of error handling in your code. If you're checking for exceptions at the same rate you'd be checking for status codes, exceptions are pointless.

But generally, your suggestion to change the architecture points to what the problem with these "ergonomic" changes engender. You more or less never have to rearchitect or refactor things when using status codes, and rearchitecting and refactoring are error prone and generally hugely fraught. The cognitive load of things like exceptions leads to lower productivity or lower quality, because you only have so many brain cycles and something has to give.

I think ergonomic changes are helpful to get more dynamic programmers into stricter languages like Rust. But I think we should keep in mind that typing and boilerplate are really never our problems and prioritize accordingly.

Re:Dark side of ergonomics in Rust by zaphar on Saturday April 14, 2018 @11:08AM (#16837620)
Any solution that boils down to just get everyone who writes code you have to interact with to always write the code correctly is doomed to failure.

Rust tries to make it impossible in the general cases to do the wrong thing. And gives you an unsafe escape hatch for when you need to do something the compiler won't allow. The result is vastly safer code because the compiler guards against the most typical human failings.

Re:Dark side of ergonomics in Rust by heavenlyhash on Saturday April 14, 2018 @06:17AM (#16836724)
> For exceptions to work predictably, all mutation within a try-catch block should be captured in a transaction that can be rolled back.

Wouldn't that just be beautiful?

Re:Dark side of ergonomics in Rust by the_grue on Saturday April 14, 2018 @08:14AM (#16837005)
That sounds like a natural fit for Rust's lifetime-tracking mechanism. The compiler can automatically drop all initialized objects upon exception. In fact, I'm reasonably sure it does just that for panic handling.
Re:Dark side of ergonomics in Rust by panic on Saturday April 14, 2018 @08:34AM (#16837043)
That example is initialization code, but you'd run into the same problem even if the control were already initialized (and you were trying to set new properties on it). In that case, you'd likely have a borrowed pointer, so it wouldn't be dropped when the panic happened.
Re:Dark side of ergonomics in Rust by ordu on Saturday April 14, 2018 @09:40AM (#16837274)
The pointer can be dropped. The pointed object cannot. So the problem of rolling back the transaction changes ownership, now our button should do sensible things in setLabel, to rollback changes made before an exception.
Re:Dark side of ergonomics in Rust by ben0x539 on Saturday April 14, 2018 @10:02AM (#16837344)
imo the dream of exceptions is deferring error handling so happy-path code can be straightforward and free of worries, but if every line of code indirectly under a try-catch block has to embody transactional thinking to enable proper RAII behavior, that seems like a huge obligation.
Re:Dark side of ergonomics in Rust by emn13 on Saturday April 14, 2018 @01:10PM (#16838244)
So, that's the bad-stuff perspective. But transactions in actual DB's show it doesn't have to be that way, and transaction isolation is likely to be easier in a language than a DB because you typically communicate via a fairly narrow, well-defined channel, and not via a morass of side-effects.

Another good-stuff perspective are processes or equivalents (e.g. erlang's internal processes or docker containers). Happy-path code can ignore errors; sufficiently serious errors tear down the "process" and the calling code can decide how to deal with that (ideally without cleanup responsibilities).

As long as indirect effects are well-contained, there's nothing necessarily wrong with ignoring errors. Especially when fine-grained error handling simply isn't interesting, it can be a relief simply to ignore errors. E.g. DB style fine-grained locking is really only possible because you don't need to manually deal with each and every possible failure moment (because there are unbelievably many).

Re:Dark side of ergonomics in Rust by ben0x539 on Sunday April 15, 2018 @08:37AM (#16842406)
yeah i agree that seems like a generally desirable state of affairs.

not sure if i'd sign the statement "you typically communicate via a fairly narrow, well-defined channel, and not via a morass of side-effects" though ;)

Re:Dark side of ergonomics in Rust by emn13 on Sunday April 15, 2018 @02:26PM (#16844073)
Hey, it's aspirational ;-).
Re:Dark side of ergonomics in Rust by anacrolix on Saturday April 14, 2018 @10:24AM (#16837425)
Haskell's STM monad.
Re:Dark side of ergonomics in Rust by fooker on Saturday April 14, 2018 @03:57PM (#16839141)
>For exceptions to work predictably, all mutation within a try-catch block should be captured in a transaction that can be rolled back.

[code] try { system("rm foo.txt");} catch (...) {} [/code]

Not everything can be rolled back. Often, automatic rollbacks lead to states unaccounted for by the developer.

Re:Dark side of ergonomics in Rust by Too on Saturday April 14, 2018 @01:51PM (#16838461)
That is not RAII, that example is Acquiring the Resource before Initializing it.
Re:Dark side of ergonomics in Rust by jhasse on Saturday April 14, 2018 @12:41PM (#16838095)
Solution: Button::create() returns a unique_ptr which you have to move to the window at the end.
Re:Dark side of ergonomics in Rust by golergka on Saturday April 14, 2018 @01:18PM (#16838272)
The problem is that you can have half-initialzed objects at all. I don't know Rust very well yet, but in a typical OOP language it's a constructor's job to completely initialize an object, and exception in constrctor causes object to not be created at all.
Re:Dark side of ergonomics in Rust by steveklabnik on Saturday April 14, 2018 @01:22PM (#16838291)
Rust does not have constructors as a language feature like that, and you must fully initialize objects, unless you use unsafe.
Re:Dark side of ergonomics in Rust by golergka on Saturday April 14, 2018 @02:52PM (#16838809)
Wouldn't you want, in a similar manner, a single create() call to be responsible for full initialization? An API that makes it possible to have entities half-initialzed looks like a design mistake to me. If an initialization is long and requires a lot of parameters, I personally split a "config" object that has easy access to all of them and the "real" object that swallows it in constructor or "create" call all at once.
Re:Dark side of ergonomics in Rust by Fronzie on Saturday April 14, 2018 @06:15AM (#16836718)
If setLabel() throws an exception, the scope is left, cleaning up button, which then no longer exists. The only try/catch blocks are where you can do anything usefull. That's typically only at the highest level of the application.

Exceptions depends on RAII, that's true.

Re:Dark side of ergonomics in Rust by panic on Saturday April 14, 2018 @06:19AM (#16836728)
The button has to exist after leaving the scope, since the whole point is to create it and add it to the window. It can't remove itself when the function returns.

You could make this general idea work, though, by adding a "button.commit();" at the end of the function. The button destructor would remove itself unless commit() had been called -- basically an ad-hoc transaction on the level of a single button.

Re:Dark side of ergonomics in Rust by twic on Saturday April 14, 2018 @07:45AM (#16836928)
Looking at this from a Rusty point of view, ownership rules make this possible. When this function creates but button, it owns it. To mutate it (addLabel/setAlignment/sizeToFit), it has to own it. But for it to continue to exist after the function returns, something else has to own it. Therefore, Window::addSubview has to take ownership of the button, and so, has to come last:

  fn make_button(window: &mut Window) {
      let mut button = Button::create();
      button.set_label("test");
      button.set_alignment(Alignment::TopLeft);
      button.size_to_fit();
      window.add_subview(button);
  }
(complete toy example at https://play.rust-lang.org/?gist=cb1db59a3ee1f4bad0a5472267c... )

In that situation, the button's destructor absolutely could delete itself.

Re:Dark side of ergonomics in Rust by alkonaut on Saturday April 14, 2018 @06:45AM (#16836775)
I know it's a toy example, but an exception in such a method doesn't seem like something that can be handled/retried like an IO problem. Any such exception should probably be handled by simply tearing down the app anyway.