SE-0430 (second review): `sendable` parameter and result values

I am trying to illustrate why making borrowing sendable mean preserving is an active special case that expands the language rules around borrowing.

If borrowing sendable (e.g. where we are being explicit about sendable) implies preserving... when sendable behavior is implicit... shouldn't borrowing also imply preserving? With my naive programmer hat on, I would expect that borrowing should work the same around any type of "transferring" semantics.

In a sense we are saying that borrowing when transferring occurs is just a normal ownership semantic (with normal transferring semantics) unless it is paired with sendable in which case there is a different rule (where we preserve).

1 Like

Okay, I think I have a better understanding of where you're coming from now, thanks! I can definitely see what you're saying, and might be able to be convinced that it's the right way to go with a bit more thought/discussion, but I'm not totally convinced. The fact that sending implies consuming already illustrates that there's some sort of conceptual linkage here, IMO. Just as you're wary of introducing a special case to borrowing, I'm wary of introducing a separate preserved concept which could be modeled as the composition of existing concepts: e.g. borrowing sendable composed explicitly borrows the value, but also 'borrows' the value's region.

So that's why I'd prefer to punt on this question and leave the meaning of borrowing sending as a future direction. If I've misunderstood and borrowing sending is not almost useless as previously described, i.e., we'd be missing out on some major piece of functionality if we didn't include it in this proposal, then I'd be much more amenable to revising my opinion here. But I'm not hearing any urgency to support borrowing sending except as a compatibility thing: in the example you posted borrowing sending doesn't appear in source (even though the region transfer semantics do).

I think sending would be a better choice than sendable, both given that it is distinct from @Sendable and Sendable in function and meaning, and that it is more consistent with borrowing/consuming. Plus func eat(_ banana: sending Banana) {} reads better than sendable would, IMO.

5 Likes

FWIW, as someone who's initially supported sending in the first review too, my intuition wasn't really based on aligning the keyword with borrowing/consuming, but more on the superficial flair of "what the function does" — it sends a value which is suitable for being sent to a different isolation region. It just reads well to me: I understand foo(sending bar: Bar) as a function that takes bar and "sends it somewhere else". Arguably, this is exactly the same thing that the original transferring conveyed, the only difference being that it still relates to the existing S/send* family of keywords.

I'm trying to underline this because when reading code, my bet is that people do not necessarily think of such -ing parameter annotations as type modifiers, but as "actions": while an @escaping closure is, strictly speaking, a subtype, I'm more convinced that, on average, people think that "the closure does something / there's somethig being done to the closure" (it escapes) than "it's a closure which is allowed to escape" — @escapable would have conveyed the latter better.

All in all, the precedent for specifically sending is in my eyes twofold:

  • send* is the only relevant and already established "concurrency-y" wordstem in Swift to date, and
  • -ing, by precedent, works the best as the suffix for conveying both "being done to by the accepting function" and "is able to be passed to the accepting function".

While I understand that the precise motivation against sending was in conveying that a value has to be innately sendable (a property), I'm again more convinced that, in context, programmers will be benefitting much more from the "action being done to" perspective (an action enabled by the property).

2 Likes

I'll perhaps also repeat my other readability/intuition argument: for a value to be "sent", it either has to be:

  • innately Sendable/@Sendable, or
  • be explicitly sent by a function that does so.

The first one is clearly a property (expressed by protocol conformance), while the latter is an action. Of course, the action can only be done to values that permit this, but here it's the function that declares this requirement (read as "I will be sending/escaping this parameter, so you better conform") rather than the value.

1 Like

Thank you all for your highly constructive feedback during this review! The Language Steering Group has decided to accept with the following modifications:

  • The sendable keyword is changed to sending.

  • The proposed borrowing sending feature has been removed; this combination of keywords will instead be rejected by the compiler unless a future proposal assigns a meaning to it.

For more details or to discuss this outcome, please see the announcement thread.

12 Likes