Live data from Hacker News

Rust RAII is better than the Haskell bracket pattern

snoyman.com

31–40 of 156 posts

Re: Rust RAII is better than the Haskell bracket pattern

#31
post #27

Earlier quoted context omitted.

> I don't think Rust can notify on a failing destructor other than panic!ing. Much as in C++, this is not really allowed: drop runs during panic unwinding, a panic during a panic will hard-abort the entire program. > I don't know if there's an elegant way to solve this. I don't really think there is. Maybe opt-in linear types could be added. That would be at the cost of convenience (the compiler would require explici…

>Much as in C++, this is not really allowed: drop runs during panic unwinding, a panic during a panic will hard-abort the entire program. Right, I didn't really consider that a "drawback" because I'm in the camp that considers that panic! shouldn't unwind but actually abort the process here and there anyway. But you're right that if you rely on the default unwinding behavior panic!ing in destructors is a very bad ide…

> But you're right that if you rely on the default unwinding behavior

You do rely on the default unwinding behavior anyway at least for `cargo test`: the test framework depends on being able to catch the unwinds from `assert_eq!` and similar.

Re: Rust RAII is better than the Haskell bracket pattern

#32
post #28

Earlier quoted context omitted.

If you are consuming an API that provides an object with a destructor, you are correct, you can determine when destructors will be called. The issue is when you produce an API that contains objects with destructors. Since you are handing these entities off to unknown code, you cannot ensure that they will be dropped. This was a problem in scoped threads in Rust.

Can you please dig deeper, that I am not sure I follow. In which case in rust you cannot be sure that "the drop" will be called?

A Rc cycle causing a leak.

See the very excellent http://cglab.ca/~abeinges/blah/everyone-poops/

Re: Rust RAII is better than the Haskell bracket pattern

#33
post #28

Earlier quoted context omitted.

If you are consuming an API that provides an object with a destructor, you are correct, you can determine when destructors will be called. The issue is when you produce an API that contains objects with destructors. Since you are handing these entities off to unknown code, you cannot ensure that they will be dropped. This was a problem in scoped threads in Rust.

Can you please dig deeper, that I am not sure I follow. In which case in rust you cannot be sure that "the drop" will be called?

If there's a cycle of strong references with Rc or Arc (or shared_ptr in C++), those objects still never get dropped/have their destructors called.

Re: Rust RAII is better than the Haskell bracket pattern

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

If you really care about reliability you implement transactional semantics on your output storage: i.e. writes are not globally visible until an explicit system wide atomic commit is preformed.

The destructor would instead be in charge to perform the rollback actions on an uncommitted transaction, if any. Rollback cannot fail and indeed the system must preserve integriy even if not performed as there is no guarantee that the process will not be killed externally.

Of course if you do not care about data integrity, swallowing errors in close is perfectly acceptable.

Edit: in general destructors should only be used to maintain the internal integrity of the process itself (freeing memory, clising fds, maintaining coherency of internal datastructures), not of external data or the whole system. It is fine to do external cleanup (removing temporary files, clearing committed transaction logs, unsubscribing from remote sources, releasing system wide locks etc), but shoud always be understood to be a best effort job.

A reliable system need to be able to continue in all circumstances (replying or rolling back transactions on restart, cleaning up leftover data, heartbeating and timing out on connections and subscriptions, using lock free algos or robust locks for memoryshared between processes, etc).

Re: Rust RAII is better than the Haskell bracket pattern

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

Re: Rust RAII is better than the Haskell bracket pattern

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

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

Re: Rust RAII is better than the Haskell bracket pattern

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

Re: Rust RAII is better than the Haskell bracket pattern

#38
post #23

Earlier quoted context omitted.

> 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. However, this is different than the bracket pattern that the article is taking about. No one in the Haskell community advocates cleaning up resources (like file descriptors, etc) using only destructors.

You misunderstood me. I'm explaining why simply adopting RAII is inappropriate in Haskell, even though the author thinks it's a better approach. I've edited my comment to make this clearer.

Author of the article discusses a library - two approaches of different (parts of) libraries.

It is quite possible you may need to have RAII somewhere in Haskell code and that's where things like parametrized monads are good: http://blog.sigfpe.com/2009/02/beyond-monads.html

It is a library and I keep saying that what is usually programming language feature is just a library in Haskell.

Re: Rust RAII is better than the Haskell bracket pattern

#39
post #3

Would be interesting to know why memory leaks are possible in Rust, if RAII is so deeply integrated into language.

Memory leaks are not only possible but they are officially supported. The most obvious being `std::mem::forget` and `Box::leak`. Of course the user of these functions should usually ensure that drop is eventually called for all initialized data but there's no way to enforce this.

If you mean unintentional leaks then that is a harder problem. Others have noted ARC and RC leaks but also thread locals may (or may not) leak[0].

[0]: https://doc.rust-lang.org/std/thread/struct.LocalKey.html#pl...

Re: Rust RAII is better than the Haskell bracket pattern

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

I am always impressed by what the ocaml/Haskell people can do compared to my language of choice (scheme).

Iirc Oleg Kiselyov implemented proper delimited continuations in ocaml as a library, without touching the runtime or compiler. Something similar has been done in Haskell.

I doubt fully dependent types can be implemented in Haskell without extra help by ghc. There has been lots of work in the area, and last time I checked you could simulate DT to some degree, but it never was as powerful as the dependant types in idris. Iirc t were some edge cases where the typing became undecidable.

Post reply on HN