Expanding that to a matched pair of counterproposal sketches:
Nonescapable Reference Types
- Add
Reference<T: ~Copyable>: ~Escapable {}andMutableReference<T: ~Copyable>: ~Escapable {}types to the language. They have no API surface; you always interact with them via the language's usual syntax. MutableReference<T>is a subtype ofReference<T>and upcasts silently[Mutable]Reference<T>is a subtype of[Mutable]Reference<U>ifTis a subtype ofU, upcasts silently, and downcasts withas?- probably need to allow projecting stored properties out of references?
&lvaluesyntax createsReference/MutableReferenceas appropriate.- If
TisCopyable- A value of type
Reference<T>can be used wherever a value of typeTcan (copied out if necessary). - A value of type
MutableReference<T>, can be used wherever an lvalue of typeTcan.
- A value of type
borrowing Tparameters become syntax sugar for regular parameters of typeReference<T>- (we gain additional new functionality because now we have mutable borrowing parameters too)
Reference Accessors
(deliberately picking new keywords here to encourage discussing this without trampling over the previous mentions of borrow accessors, though I expect we'd choose borrow if this proposal were actually adopted)
- Add
referenceandmutableReferenceaccessors.var t: T { reference { ... } }returnsReference<T>var t: T { mutableReference { ... } }returnsMutableReference<T>.- providing
referencesynthesizesgetforCopyabletypes - providing
mutableReferencesynthesizesgetandsetforCopyabletypes - read-only stored properties provide
getandreference - mutable stored properties provide
get,reference,setandmutableReference reference setis a legal combination
dict[key] // borrows value, returning Reference<Value>?, all good
dict[key] = nil // calls `set` with `Value?`, works fine
dict[key]?.append(3) // works for copyable types, but not for noncopyables
// because we have to copy out of Reference<Value> to be able to call
// mutating append.
// but, we can now
// assuming func slot(key: Key) -> MutableReference<Value>?
dict.slot(key)?.append(3) // in-place update of the value
// or if `reference mutableReference set` were allowable,
// with the compiler picking contextually between
// `mutableReference` and `set`
// I think we could make both work:
dict[key] = nil // calls `set` 'cos we're replacing the whole value
dict[key]?.append(3) // calls `mutableReference` 'cos we don't need the whole value
These proposals don't address the "damage to existing swift code" criticism; it effectively splits the ecosystem. A protocol using { reference mutableReference } for efficiency can't interoperate with a type with get set. On the other hand, it does encourage people to stick with get set unless they really need the efficiency or have noncopyables...