> On Mar 7, 2017, at 11:47 AM, plx via swift-evolution
> <[email protected]> wrote:
>> On Feb 27, 2017, at 11:08 AM, John McCall <[email protected]
>> <mailto:[email protected]>> wrote:
>>
>>> On Feb 25, 2017, at 11:41 AM, plx via swift-evolution
>>> <[email protected] <mailto:[email protected]>> wrote:
>>> 1: Collection Composition
>>>
>>> As much as the high-level picture is focused on safety, I’d just like to
>>> request a strong focus on making sure the eventual ownership system
>>> addresses the common issues like avoiding copies when mutating through
>>> optionals, or when nesting collections, and so on.
>>
>> Avoiding copies is basically the entire core of the proposal. The
>> interaction with safety is that sometimes copies are necessary to avoid
>> memory unsafety, and this proposal recommends an approach that minimizes
>> that.
>
> Thanks for the extend reply and sorry for my own belated reply.
>
> What motivated me to ask for more focus on the common cases was that it was
> unclear from reading the proposal whether the `inout` binding--as used in
> `for inout`--could be used with `if`: in other words, we currently have `if
> let` and `if var`, but will we have `if inout` under this proposal?
>
> I am embarassed to admit I honestly couldn't tell if the proposal didn't
> contain any examples of `if inout` due to (a) the capability following
> trivially from the rest of the proposal or (b) the capability falling outside
> the scope of the proposal.
I just didn't think about it. "if inout" is quite supportable under the basic
model.
>> The technical obstacle to this is just that, so far, we've tried to make
>> language features like for-loops use formal protocols.
>>
>> An iteration protocol is going to have requirements like this:
>> generator iterate() -> Element
>> mutating generator iterateMutable() -> inout Element
>> But there's no valid substitution that makes '(Key, inout Value)' equal
>> either 'Element' or 'inout Element'. So we'd have to do some special to
>> make this work.
>>
>> That said, no, there's no intrinsic technical reason this can't be made to
>> work.
>
> The explanation of wanting to stick to formal protocols makes perfect sense.
> I don’t think this should be a show-stopper, but I do think it’ll be a common
> request for a subsequent enhancement.
>
> Even in the interim it seems quite feasible to emulate the capability in any
> concrete case with enough willingness to crank out boilerplate; thus e.g. if
> someone truly needs `for (index, inout value) in collection.enumerated()` or
> `for (a, inout b) in zip(as,bs)` it isn’t as if they’re entirely stuck.
I actually spent some more time thinking about it after I wrote it, and there
are some nice things that come out of abandoning formal protocols here. You
could allow generators to be overloaded, not by name, but by the "inout-shape"
of their return value, so that something like
for (a, inout b) in ...
would specifically look for a generator like:
generator() -> (T, inout U)
etc.
>>> 3: Mutable Views
>>
>> It's not sensible to have a type that conditionally conforms to a protocol
>> based on context.
>
> That did indeed seem like a big ask!
>
> I’ll put in an early request to consider
>
> @moveonly {
> struct File { }
> }
>
> …(or @scope(moveonly) or @context(moveonly) or @dialect(moveonly), etc.).
>
> It’s confusing that `moveonly` essentially applies “inward”—it flags the code
> *within* a scope as using different assumptions, etc.—in contrast to most of
> the modifiers like `open`, `final`, `mutating`, etc., apply “outward” (by
> e.g. describing how visible the code in the scope is from other scopes, etc.).
An interesting idea.
>> However, the requirements of MutableCollection are all 'mutating', so you
>> can't actually use them unless you have a mutable value. So the way to fit
>> what you're asking for into the ownership proposal is to make sure that
>> clients of your view type only have a mutable value when the base was
>> mutable, and the easiest way of getting that is to have the view be vended
>> as storage rather than a return value. If you think about it, a mutable
>> view can't be an independent value anyway, because if code like this could
>> work:
>>
>> var grid = ... // note that this is mutable
>> var view = grid.traversal(from: p, following: m) // should return a
>> mutable view by design, right?
>> view.mutate() // reflected in grid, right?
>>
>> then it completely destroys the value-type properties of 'grid', because
>> 'view' should really be an independent value.
>
> Without belaboring this, the point is indeed to “destroy the value-type
> properties of `grid`”, while trying to keep things “safe” by quarantining
> `view`—and the temporary relaxation of value semantics its existence
> implies—to a specific scope; thus under the status quo the use-site looks
> like this:
>
> var grid = // note that this is mutable
> // also note that `mutatingTraversal` is a `mutating` method...
> grid.mutatingTraversal(from: c, following: m) {
> (inout view: MutableGrid2DTraversal<T>) -> Void
> in
> // ^ this is the only public method that vends
> `MutableGrid2DTraversal<T>`, and
> // `MutableGrid2DTraversal<T>` has no public constructors, thus `view` is
> // “quarantined” to this scope unless we actively attempt to leak
> `view`...
> view.mutate()
> }
>
> …which currently has two major drawbacks:
>
> - (a) lots of code duplication: the immutable `Grid2DTraversal<T>` and the
> mutable `Mutable2DTraversal<T>`
> - (b) the “quarantine” isn’t airtight, in that there are at least these three
> ways a `view` could escape:
>
> var grid = // …
> var leakedView: MutatingGrid2DTraversal<T>? = nil
> var otherLeakedView = grid.mutatingTraversal(from: c, following: m) {
> (inout view: MutableGrid2DTraversal<T>) -> MutableGrid2DTraversal<T>
> in
> view.mutate()
> // leak #1:
> leakedView = view
> // leak #2:
> self.liveDangerously(leaking: view)
> // leak #3:
> return view
> }
>
> …which imho makes it fair to say “the above approach *encourages* ‘safe’
> usage, but can’t *prevent* unsafe usage”.
>
> Under the ownership proposal nothing changes for drawback (a): to stick with
> the design and behavior sketched above requires distinct types and thus a lot
> of duplicate or near-duplicate boilerplate.
Only if you insist that you want a traversal to behave like a linked value even
when everything about the traversing code makes it look independent. You're
trying to shoe-horn in a kind of reference semantics where it doesn't belong.
Look, your goals are an exact match for the basic ownership design here. You
have a type (the grid), it has a thing you can get from it (the traversal),
that thing has both mutating and non-mutating operations, you want the mutating
operations to be usable if and only if the original value was mutable, you
don't want to allow simultaneous accesses to the original value while you have
the thing, etc. These are exactly the properties you get by default if
starting a traversal is just protecting a component of the value via a
property/subscript access. Just accept that a let or var containing a
traversal is an independent value.
> Drawback (b) seems to fare better under the ownership proposal, provided that
> I make `MutatingGrid2DTraversal` a non-Copyable type; at least AIUI making
> `MutatingGrid2DTraversal` non-Copyable would effectively close leaks #1, #2,
> and #3, b/c:
>
> - I would explicitly have to write e.g. `leakedView = move(view)` (a *very*
> unlikely accident)
> - I would also have to supply a "replacement value” for `view` by the end of
> the scope (impossible due to lack of public constructors)
>
> …at least if I understand correctly. Is this accurate? If accurate, how
> likely would it be that “failed to provide replacement value for `view` after
> move” would get caught at compile time?
It would be guaranteed to get caught at compile time.
This leaking issue is something I didn't get into in the manifesto, but we've
actually thought a fair amount about it. Generalized accessors come up a lot
in the context of array slices, which share a lot of similar properties with
your scenario. With array slices, we have some additional problems:
- arrays are copy-on-write values, and the original array does need to keep
its buffer alive while a slice is being accessed
- slices need to be usable as independent (and copyable) value types; thus at
least sometimes they need to have a strong reference to the buffer
- forming a mutable slice really shouldn't form a second strong reference to
the buffer because then mutations of the slice will trigger a buffer copy
The current line of thinking is that maybe we can have explicit copy and move
hooks so that e.g. a projected slice could hold an unowned reference to the
buffer which would be promoted to a strong reference on move/copy. But that's
a lot of complexity, and I don't think it helps you that much.
Your analysis is correct: making the type move-only solves many of these
problems, but not all of them because of the ability to move values aside.
Rust has a concept of types that can't even be moved, I think; maybe we need to
explore that.
John.
>
> Thanks again for providing such detailed clarifications about this proposal.
>
>> The proposal suggests doing this instead with storage, so that the view is
>> logically a (mutable) component of the grid. So on the use side:
>>
>> var grid = ...
>> inout view = &grid[traversingFrom: p, following: m]
>> view.mutate()
>>
>> and on the declaration side:
>>
>> struct Grid2D<T> {
>> subscript(traversingFrom corner: Grid2DCorner, following motion:
>> Grid2DMotion) -> Grid2DTraversal<T> {
>> read {
>> yield Grid2DTraversal(...)
>> }
>> modify {
>> var traversal = Grid2DTraversal(...)
>> yield &traversal
>> // ensure changes were applied here
>> }
>> }
>> }
>>
>> If you feel that the aesthetics of this leave something to be desired,
>> that's a totally reasonable thing to discuss.
>>
>> John.
>>
>>>
>>> Anyways, it’s not clear to me, personally, whether or not the above is
>>> within the scope of any likely, concrete ownership system that’d follow
>>> from the manifesto or not…but if at all possible I’d prefer the eventual
>>> ownership system make it reasonable—and reasonably safe—to implement
>>> “small-c ‘collection’s that can safely-and-efficiently expose various
>>> *mutable* views as big-C `Collection`s”.
>>>
>>> Apologies if all of the above considerations have answers that follow
>>> trivially from the manifesto; it’s just unclear personally whether the
>>> features described in the manifesto would work together to allow something
>>> like the above to be implemented more-reasonably than currently the case.
>>>
>>>> On Feb 17, 2017, at 11:08 AM, John McCall via swift-evolution
>>>> <[email protected] <mailto:[email protected]>> wrote:
>>>>
>>>>> On Feb 17, 2017, at 4:50 AM, Adrian Zubarev
>>>>> <[email protected]
>>>>> <mailto:[email protected]>> wrote:
>>>>> Hi John, would you mind creating a markdown document for this manifesto
>>>>> in https://github.com/apple/swift/tree/master/docs
>>>>> <https://github.com/apple/swift/tree/master/docs>? :)
>>>>>
>>>>>
>>>> Yes, it should go in the repository. That commit is pending, but the in
>>>> meantime, you can see the document properly rendered at:
>>>>
>>>> https://github.com/rjmccall/swift/blob/4c67c1d45b6f9649cc39bbb296d63663c1ef841f/docs/OwnershipManifesto.md
>>>>
>>>> <https://github.com/rjmccall/swift/blob/4c67c1d45b6f9649cc39bbb296d63663c1ef841f/docs/OwnershipManifesto.md>
>>>>
>>>> John.
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> [email protected] <mailto:[email protected]>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> [email protected] <mailto:[email protected]>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
> _______________________________________________
> swift-evolution mailing list
> [email protected] <mailto:[email protected]>
> https://lists.swift.org/mailman/listinfo/swift-evolution
> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution