[Pitch] Modify and read accessors

Expanding that to a matched pair of counterproposal sketches:

Nonescapable Reference Types

  • Add Reference<T: ~Copyable>: ~Escapable {} and MutableReference<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 of Reference<T> and upcasts silently
  • [Mutable]Reference<T> is a subtype of [Mutable]Reference<U> if T is a subtype of U, upcasts silently, and downcasts with as?
  • probably need to allow projecting stored properties out of references?
  • &lvalue syntax creates Reference/MutableReference as appropriate.
  • If T is Copyable
    • A value of type Reference<T> can be used wherever a value of type T can (copied out if necessary).
    • A value of type MutableReference<T>, can be used wherever an lvalue of type T can.
  • borrowing T parameters become syntax sugar for regular parameters of type Reference<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 reference and mutableReference accessors.
    • var t: T { reference { ... } } returns Reference<T>
    • var t: T { mutableReference { ... } } returns MutableReference<T>.
    • providing reference synthesizes get for Copyable types
    • providing mutableReference synthesizes get and set for Copyable types
    • read-only stored properties provide get and reference
    • mutable stored properties provide get, reference, set and mutableReference
    • reference set is 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...

1 Like