~Escapable, Span, Ownership Annotations, etc

To be clear (answering @filip-sakel and clarifying for @Dmitriy_Ignatyev), ~Escapable is not the primary problem. The primary problem is the steady march toward a full Rust-like lifetime system, which is where you end up as soon as you start returning (instead of yielding) notional references from one thing into another. We believe escapability shouldn't be surfaced as a first-class property of types. All that said, ~Escapable seems to be entirely unneeded today.

I was surprised to find that it seems to be possible today without any explicit treatment of escapability.

With ~Escapable, we can create slices that cannot outlive their storage, removing this hazard. Slices aren’t a marginal or narrow feature, they’re broadly used. The same applies to other temporary views: substrings, windowed computations, cursors... anything that should not escape its underlying data.

All of which you can do by yielding ~Copyable things.

Swift already distinguishes between escaping and non-escaping closures, with non-escaping closures being extremely common. ~Escapable extends this concept to other types, allowing safe, temporary borrows in a broader range of situations.

None of this is lost on me, you know. I saw this complexity coming years ago when I pleaded for an effort to address fundamentals first and use these existing concepts to create a more coherent solution.

It's mostly number 2 ;-). As I mentioned, some notion of escapability is needed. We think it shouldn't be surfaced in this way (and arguably I've shown that it doesn't need to be—it can just be a consequence of other things), but this one pseudo-protocol, by itself, doesn't create that much of a problem.

Of course, all of these features add complexity. However, we’re not forced to use them.

I'll just point out that the same argument is often given by proponents of C++ when people say it's too complicated. Complexity adds up, hurting usability, teachability, implementability, evolvability, understandability, and adoption (…I could go on…and on!) of a language. These effects are well understood among language designers. When we were first designing Swift, we stayed keenly aware of that fact and pushed back hard against the introduction of new wrinkles. The larger point is that this ethos seems to have been mostly lost. The number of partially-implemented, broken, or limited features in @Jon_Shier's list seem to reflect that.

When you're thinking about all these generalized uses of non-escapability, keep in mind that what you get from putting it in the type system is limited in the same way Rust references are (though not any more limited than yielded things). The compiler has to reason about what might escape based on only function signatures, so it's easy to come up with useful things you might want to do where nothing actually escapes but the compiler has to stop you. For example, in the Hylo implementation we intentionally store substrings of the whole file string all over the place, and we store them far away from that whole file string. Doing that with ~Escapable types would be impossible. To track the lifetime relationships for a span-like type would entail a Rust-like lifetime system and would require threading lifetime parameters through many diverse data structures.

6 Likes