Hacker News new | ask | show | jobs
by aturon 3521 days ago
The key problem: in-memory data structures can embed ownership of system resources.
1 comments

I'd take an approach inspired by Standard ML's eqtypes. In Standard ML:

(0) Some types are eqtypes (akin to Rust types that implement the Eq trait, but managed entirely by the language, you can't define custom Eq impls).

(1) If a type constructor is an eqtype, then the result of applying it to an eqtype is an eqtype, but the result of applying it to a non-eqtype is a non-eqtype. For example, “list of ints” is an eqtype, but “list of functions” isn't.

Similarly, I propose that:

(0) Some types are copytypes (akin to Rust types that implement the Copy trait, again, managed entirely by the language).

(1) If a type constructor is a copytype, the result of applying it to a copytype is a copytype, but the result of applying it to a non-copytype is a non-copytype. For example, “list of ints” is a copytype, but “list of file objects” isn't.

Subtleties:

(0) If we have first-class functions, functions must be parameterized over whether they can be called more than once (akin to the distinction between FnOnce and Fn in Rust).

(1) When a value goes out of scope, its destructor is called. For copytypes, the destructor is guaranteed to be trivial, and can be optimized away. For “base non-copytypes” (e.g., file objects), the destructor is explicitly implemented by the programmer. For “derived non-copytypes” (e.g., lists of file objects, closures that have captured file objects), the destructor is automatically generated, and it does the obvious thing (destroy all file objects in the list, or captured by the closure).

FWIW, Copy already behaves like that in Rust: a "custom" Copy impl is just opting in to the default copy implementation, there's no way for the programmer to inject any code.

The reason the impl is required is to ensure people write what they mean: it is backwards incompatible to go from Copy to non-Copy, so it would be unfortunate for a type to accidentally be Copy because an early version of the type happened to only contain Copy types. (The trait is really just a marker for "this type can be safely duplicated with memcpy".)

The Send and Sync traits are similar, and are in fact almost identical to eqtypes in that an explicit implementation is not required.

> Copy already behaves like that in Rust: a "custom" Copy impl is just opting in to the default copy implementation

Yeah, I realized that, then deleted that part of my post.

> The reason the impl is required is to ensure people write what they mean: it is backwards incompatible to go from Copy to non-Copy, so it would be unfortunate for a type to accidentally be Copy because an early version of the type happened to only contain Copy types.

In my proposal, with an ML-style module system, you can define an abstract non-copytype whose internal representation is a copytype, just like in Standard ML you can define an abstract non-eqtype whose internal representation is an eqtype.