> On Mar 7, 2017, at 10:02 AM, John McCall via swift-evolution
> <[email protected]> wrote:
>
>>> 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]> wrote:
>>>>
>>>> On Feb 25, 2017, at 11:41 AM, plx via swift-evolution
>>>> <[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.
Would it be valid write:
```
var iter = foo.makeIterator()
inout x = iter.next()
```
If not, are special case `inout` returns really the right way to model this
feature? It seems that a inout-map interface would be better as it doesn't
require any special case language features.
Or am I misunderstanding, and `inout` reruns in the general case are supported
by this proposal? If so, `inout` seems like a poor name.
>
>>>> 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]> wrote:
>>>>>>
>>>>>> On Feb 17, 2017, at 4:50 AM, Adrian Zubarev
>>>>>> <[email protected]> wrote:
>>>>>> Hi John, would you mind creating a markdown document for this manifesto
>>>>>> in 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
>>>>>
>>>>> John.
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> [email protected]
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> [email protected]
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected]
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution