Live data from Hacker News

Rust RAII is better than the Haskell bracket pattern

snoyman.com

41–50 of 156 posts

Re: Rust RAII is better than the Haskell bracket pattern

#41
post #2

The thing is that, in Haskell, even when you attach a function to run during destruction, the runtime doesn't guarantee that the function will be called promptly, or even at all. Rust drops (runs destructor and deallocates) values as soon as they go out of scope; C++ too. In Haskell you depend on the whims of the GC, which makes RAII unusable. (The Haskell approach of not guaranteeing destructors being called does ha…

> when many C++ and Rust programs are about to end, they spend the last few cycles uselessly deallocating memory that would've immediately been freed via _exit(2)

thank god they do this. how many times did I have to manually force linux to release sockets because badly coded C programs which opened sockets forgot to release them causing them to hang up for ~5 minutes after the process ended. With proper RAII classes this does not happen.

Re: Rust RAII is better than the Haskell bracket pattern

#42
post #10

How do you handle errors at resource release? When you close a file, the final writes take place, and they can fail. What's the idiom in Rust for getting them out? Python's "with" clause, and the way it interacts with exceptions, is the only system I've seen that gets this right for the nested case.

How do you properly handle an error on fclose in C?

At least without pretending that everything's OK. Report to user and abort process would be appropriate for many cases. Continuing work with half-written data without ever noticing it is not appropriate.

Re: Rust RAII is better than the Haskell bracket pattern

#43
post #2

The thing is that, in Haskell, even when you attach a function to run during destruction, the runtime doesn't guarantee that the function will be called promptly, or even at all. Rust drops (runs destructor and deallocates) values as soon as they go out of scope; C++ too. In Haskell you depend on the whims of the GC, which makes RAII unusable. (The Haskell approach of not guaranteeing destructors being called does ha…

> when many C++ and Rust programs are about to end, they spend the last few cycles uselessly deallocating memory that would've immediately been freed via _exit(2) thank god they do this. how many times did I have to manually force linux to release sockets because badly coded C programs which opened sockets forgot to release them causing them to hang up for ~5 minutes after the process ended. With proper RAII classes…

That has nothing to do with deallocating memory. Of course there are other kinds of resources which are not automatically freed when a program exits.

Re: Rust RAII is better than the Haskell bracket pattern

#44
tl;dr - Try rust.

The mechanic point of this article is pretty clear:

- it's possible to be unsafe in both Haskell and Rust when dealing with resource cleanup

- Rust does a bit of a better job in the general case though it has it's own warts (see the other comments, it's hard to deal with issues during `drop`-triggered cleanup)

I want to make a muddier meta point -- Rust is the best systems language to date (does anyone know a better one I can look at?).

- The person who wrote this article Michael Snoyman[0] is mainly a haskell developer, he's the lead developer behind arguably the most popular web framework, yesod[1].

- Haskell developers generally have a higher standard for type systems, and spend a lot of time (whether they should or not) thinking about correctness due to the pro-activity of the compiler.

- These are the kind of people you want trying to use/enjoy your language, if only because they will create/disseminate patterns/insight that make programming safer and easier for everyone down the line -- research languages (Haskell is actually probably tied for the least "researchy" these days in the ML camp) are the Mercedes Benz's of the programming world -- the safety features trickle down from there.

- Rust is not a ML family language -- it's a systems language

- People who write Haskell on a daily basis are finding their way to rust, because it has a pretty great type system

When was the last time you saw a systems language with a type system so good that people who are into type systems were working with it? When was the last time you saw a systems language that scaled comfortably and gracefully from embedded systems to web services? When have you last seen a systems language with such a helpful, vibrant, excited community (TBH I don't think this can last), backed by an organization with values Mozilla's?

You owe it to yourself to check it out. As far as I see it rust has two main problems:

- Learning curve for one of it's main features (ownership/borrowing)

- Readability/Ergonomics (sigils, etc can make rust hard to read)

Admittedly, I never gave D[2] a proper shake, and I've heard it's good, but the safety and the emphasis on zero-cost abstractions Rust offers me makes it a non-starter. Rust is smart so I can be dumb. C++ had it's chance and it just has too much cruft for not enough upside -- there's so much struggle required to modernize, to make decisions that rust has had from the beginning (because it's so new). It might be the more stable choice for a x hundred people big corporate project today or next month, but I can't imagine a future where Rust isn't the premier backend/systems language for performance critical (and even those that are not critical) programs in the next ~5 years.

I'll go even one step further and say that I think that how much rust forces you to think about ownership/borrowing and how memory is shared around your application is important. Just as Haskell might force you to think about types more closely/methodically (and you're often better for it), Rust's brand of pain seems instructive.

[0]: https://www.snoyman.com/

[1]: https://www.yesodweb.com/

[2]: https://dlang.org/

Re: Rust RAII is better than the Haskell bracket pattern

#45
post #10

How do you handle errors at resource release? When you close a file, the final writes take place, and they can fail. What's the idiom in Rust for getting them out? Python's "with" clause, and the way it interacts with exceptions, is the only system I've seen that gets this right for the nested case.

I do prefer the "with"/"try-with-resources" approach because it is explicit.

With RAII in C++ there's no visual difference between dumb data objects and objects like locks that are created and held on to mainly to cause implicit side effects.

In Rust this also prevents the compiler from dropping objects early - everything must be held until the end of its scope for the 0.1% of cases where you're RAII managing some externally visible resource. In those cases I would like the programmer to denote "The exact lifetime of this object is important", so the reader knows where to pay attention.

Re: Rust RAII is better than the Haskell bracket pattern

#46
You can also have the exact opposite problem with RAII, where a resource survives the end of a transaction, because there is still a live reference to it hidden away somewhere (say, due to some debugging code holding on to it).

This is a classical liveness vs. safety dualism. "Something good will eventually happen" and "nothing bad will ever happen" are promises whose solutions are often in conflict with one another.

The general problem — to make transactional state changes and transactional control flow (i.e. expectations about these state changes) match up precisely — is not easy to solve in the general case, especially once you move on to things that are less trivial than simple resource acquisition/release matching.

Re: Rust RAII is better than the Haskell bracket pattern

#47

You can also have the exact opposite problem with RAII, where a resource survives the end of a transaction, because there is still a live reference to it hidden away somewhere (say, due to some debugging code holding on to it). This is a classical liveness vs. safety dualism. "Something good will eventually happen" and "nothing bad will ever happen" are promises whose solutions are often in conflict with one another.…

That's also a problem with garbage collection, by the way. GC means memory safety, but not necessarily correctness. In fact, it invites sloppiness, and a kind of sloppiness that sits at a more conceptual level, which could be harder to fix.

Re: Rust RAII is better than the Haskell bracket pattern

#48
post #35

Earlier quoted context omitted.

Please, don't add them to the language. Use the library approach instead, it is much more Haskellish.

Is it possible? I mean, to add linear types via a library? I feel like it would have been done already if it were.

Linear types amounts to modification of environment - "use" of linear value removes it from environment, so you can't eat cake and still have it. If you look at the use of unique types in Clean, you will see that their use closely reminds monadic code (e.g. "#" syntax). Otherwise you will need to invent variable names like handle1, handle2, handle3... to refer of "modified" handles generated after each use.

You can easily simulate that using parametrized monad: http://blog.sigfpe.com/2009/02/beyond-monads.html

E.g., hClose will have type like (Has listIn h, listOut ~ Del h listIn) => h -> ParamMonad listIn listOut () and hGetLine will result in type much like this one: (Has list h) => h -> ParamMonad list list String

It is not perfect: you still may have reference to handle after it's use and you may be tempted to introduce it somehow back and get run-time error; you also would struggle juggling two handles for copying files, for example (for this you may have to use ST parametrization trick).

But anyway, you really not need linear types all that often (they are cumbersome - I tried to develop language with linear types, type checking rules and types get unwiledy very soon) and when you do, you can have a library. Just like STM, parsers, web content generation/consumption, etc. Linear types do not warrant language change.

Re: Rust RAII is better than the Haskell bracket pattern

#49
post #10

How do you handle errors at resource release? When you close a file, the final writes take place, and they can fail. What's the idiom in Rust for getting them out? Python's "with" clause, and the way it interacts with exceptions, is the only system I've seen that gets this right for the nested case.

How do you properly handle an error on fclose in C?

The same way you would handle an error on fwrite() or fflush(). Any of them failing means that the data wasn't written correctly; what to do with that depends on the program.

Re: Rust RAII is better than the Haskell bracket pattern

#50
post #2

The thing is that, in Haskell, even when you attach a function to run during destruction, the runtime doesn't guarantee that the function will be called promptly, or even at all. Rust drops (runs destructor and deallocates) values as soon as they go out of scope; C++ too. In Haskell you depend on the whims of the GC, which makes RAII unusable. (The Haskell approach of not guaranteeing destructors being called does ha…

> when many C++ and Rust programs are about to end, they spend the last few cycles uselessly deallocating memory that would've immediately been freed via _exit(2) thank god they do this. how many times did I have to manually force linux to release sockets because badly coded C programs which opened sockets forgot to release them causing them to hang up for ~5 minutes after the process ended. With proper RAII classes…

Your example combined with the parents observation show that C++ put under the same construct the concepts that should be separated: memory allocation should be handled differently from the construction, destruction and other resource allocation.
Post reply on HN