Live data from Hacker News

Rust RAII is better than the Haskell bracket pattern

snoyman.com

51–60 of 156 posts

Re: Rust RAII is better than the Haskell bracket pattern

#51
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…

Do you mean orphaned sockets, stuck in FIN_WAIT?

Surely what objects are are meant to do is call shutdown(2) syscall - or shutdown(3) C library function - on the socket in their destructor or whatever to prevent that. But I don't think the same applies for memory, once the process is destroyed the kernel should reclaim all memory in the process page tables automatically. Otherwise you'd end up with a pretty trivial way of disabling the system by exhausting all the memory...

Re: Rust RAII is better than the Haskell bracket pattern

#52
post #35
post #15

Something to keep in mind - linear types are on their way[1], with exactly this usecase in mind. Simon Peyton Jones gave an excellent presentation on the topic[2], briefly discussing exceptions, as well as giving a mention to ResourceT and the phantom type solution in the article (described as channel-passing). [1] https://arxiv.org/abs/1710.09756 [2] https://www.youtube.com/watch?v=t0mhvd3-60Y

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

Haskell has such an extensive set of language extensions, I would say adding new features to the type system is probably the MOST Haskell-ish way of doing things.

The explicit purpose of Haskell is to be a basis for research into functional language design (edit: among other purposes). By "explicit purpose" I mean exactly that... people got together in 1987 to come up with a language for research. Haskell was never supposed to ossify into some kind of "finished product", it was built exactly so people could experiment with things like linear types. If you want to just write libraries and get stuff done with a more or less fixed language, you probably want to be writing OCaml.

I mean, just look at the list of GHC extensions... there are something like a hundred of them! The list is growing longer every year. https://downloads.haskell.org/~ghc/latest/docs/html/users_gu...

Re: Rust RAII is better than the Haskell bracket pattern

#53

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

Oddly, Rust's ownership system really does solve these problems, and Non-lexical lifetimes should eliminate accidental scope-broadening. Unless you are doing some mega-schenanigans, an e.g. MutexGuard gets released precisely when you think.

Your point about this being difficult to solve in the general case is true, it's just worth pointing out Rust intends to do that hard thing anyway.

Re: Rust RAII is better than the Haskell bracket pattern

#54

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 any…

I have trouble giving D a proper shake because of how rarely I find it to be the best solution to a problem.

Re: Rust RAII is better than the Haskell bracket pattern

#55

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

Can you give in an example?

Keeping a debug reference at end of transaction (via a shared-reference type, since a non-shared RAII reference type could never get into that state) isn't a coding error, it's a design error -- the development intentionally requested contradictory things. That is solved by using weak references if you don't want a debug tool to force an object to stay alive.

Re: Rust RAII is better than the Haskell bracket pattern

#56
post #35
post #15

Something to keep in mind - linear types are on their way[1], with exactly this usecase in mind. Simon Peyton Jones gave an excellent presentation on the topic[2], briefly discussing exceptions, as well as giving a mention to ResourceT and the phantom type solution in the article (described as channel-passing). [1] https://arxiv.org/abs/1710.09756 [2] https://www.youtube.com/watch?v=t0mhvd3-60Y

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

What is "the library approach"? Library approaches have failed.

See http://www.well-typed.com/blog/2016/09/sharing-conduit/

Re: Rust RAII is better than the Haskell bracket pattern

#57
post #53

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

Oddly, Rust's ownership system really does solve these problems, and Non-lexical lifetimes should eliminate accidental scope-broadening. Unless you are doing some mega-schenanigans, an e.g. MutexGuard gets released precisely when you think. Your point about this being difficult to solve in the general case is true, it's just worth pointing out Rust intends to do that hard thing anyway.

Non-lexical lifetimes do not affect something that implements Drop, so the MutexGuard will, by default, last till the end of its lexical scope.

You can still call drop on it manually to release it earlier, though.

Re: Rust RAII is better than the Haskell bracket pattern

#58

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 any…

> When was the last time you saw a systems language that scaled comfortably and gracefully from embedded systems to web services?

Have a look at ATS[1], it supports many features that are available in Rust, and let you build proofs about your code behaviour. It's quite type annotation heavy though iirc, but it's very efficient.

[1] : http://www.ats-lang.org

Re: Rust RAII is better than the Haskell bracket pattern

#59

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

Can you give in an example? Keeping a debug reference at end of transaction (via a shared-reference type, since a non-shared RAII reference type could never get into that state) isn't a coding error, it's a design error -- the development intentionally requested contradictory things. That is solved by using weak references if you don't want a debug tool to force an object to stay alive.

It is not a design error. You are making the mistake of equating reachability of a reference with making it legal to access it. And the problem is that the two often aren't one and the same.

As a simple example, you may still want to access a resource after it has been released. Closing a network connection, for example, does not mean that accessing it is invalid. The connection may still have state (such as statistics collected or whether a non-blocking close was clean) that is perfectly legal to access after release (and in fact may only be consistent/observable afterwards).

The Eiffel FILE class [1], for example, allows you to call `is_closed` at any time (as well as the various `is_open` functions). This is necessary because `not is_closed` is evaluated as a precondition for many other operations.

A more complex example is a resource that is shared by many threads. Whether that resource is valid is often not a question of whether a reference is reachable, but a function of complex distributed state. Sometimes this can be solved by atomic reference counting, but even then atomic reference counting is expensive.

[1] https://archive.eiffel.com/products/base/classes/kernel/file...

Re: Rust RAII is better than the Haskell bracket pattern

#60

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.

At least in java the recommended way is to only use GC as safety net for resource management. Print a warning when it's cleaned up due to finalizers (or cleaner-refs since v9) and use scope-based cleaning instead where possible.
Post reply on HN