An attempt to deal with the multiple dimensions of projection mutability in Hylo, particularly those raised by slicing.
Right now we have strict coupling between the binding of the receiver of a projection and the properties of what is projected:
accessor | receiver restrictions | projected value |
---|---|---|
let | - | let-bound |
inout | mutable | inout-bound |
sink | last use (maybe before resurrection) | rvalue |
set | n/a |
If a sink
accessor is used, the receiver is consumed. Otherwise it
is bound per the accessor during the lifetime of the projected value.
This arrangement can't express some things we care about.
Whenever a fully-ephemeral value (one with no remote parts) is
projected, it currently appears as an lvalue via let
or inout
accessors, but it could also be exposed as a fully owned value for
reading.
Swift calls this accessor get
, and I propose using the same keyword
in Hylo. A let
accessor can be synthesized from a get
accessor
(and it probably makes sense to forbid writing both, as Swift does
with get
/_read
).
In Hylo, rvalues are mutable, but given that projections expose
notional parts, it's important that projected rvalues are not
mutable, or it would be easy to appear to mutate a part of a thing
with no effect. You can always make a var
binding if you want to
mutate them.
When a slice is projected out of an immutable value, we want it to be
mutable so we can pop elements off the front or back, while its
elements are immutable. That in turn suggests the need for separate
Slice
and SliceWithMutableElements
(associated, for full
flexibility) types.
trait Collection<Element> {
associatedtype Element
associatedtype Slice: Collection<Element>
where Slice.Position == Position
subscript(_ Range<Index>): Slice { nonmutating inout }
}
trait MutableCollection<Element>: Collection<Element> {
associatedtype Element
associatedtype Position
associatedtype SliceWithMutableElements: MutableCollection<Element>
where SliceWithMutableElements.Position == Position
subscript(_ Range<Index>): SliceWithMutableElements { inout }
}
Note:
- A new kind of accessor (strawman syntax:
nonmutating inout
) is needed to indicate a thing that cannot escape the projection, is mutable, but whose mutations do not affect the receiver of the subscript. - There are no
get
orlet
accessors here at all. We want the subscript ofCollection
to be used when a immutableMutableCollection
is sliced. This is at least unusual; Swift requiresget
orlet
(_read
in Swift) access when there isinout
. We could renamenonmutating inout
mutable let
to hide this weirdness, but then one can take aninout
binding to the result of amutable let
access. - A subscript overload is required because the slice types are
distinct. It's not just a matter of extending
Collection
's subscript with a new kind of accessor.