> On Apr 23, 2016, at 1:27 AM, Pyry Jahkola via swift-evolution > <[email protected]> wrote: > > I'd like to second James Campbell's suggestion of a `mutate` keyword. > Clarifying comments inline below: > >> On 23 Apr 2016, at 00:24, Dave Abrahams via swift-evolution >> <[email protected] <mailto:[email protected]>> wrote: >> >> This is not a new idea. Something almost identical to this has been >> explored and discussed quite thoroughly already: >> <https://github.com/apple/swift/blob/master/docs/proposals/Inplace.rst >> <https://github.com/apple/swift/blob/master/docs/proposals/Inplace.rst>>. >> In fact, it was implmented and later reverted because it raised >> language-design questions for which we had no good answers. > > I don't know if the following are particularly good answers, but I'll try > anyway: > >> I don't believe the choice of glyph (& vs =) affects any of the >> fundamental issues: >> >> * Should the x.=f() syntax be required for *every* mutating method >> invocation? > > Allow me to ask it differently: Should some specific syntax be required for > every mutating method? — Yes. > > Should the syntax be `x.=f()`? — Not necessarily. I kinda like James > Campbell's idea of a `mutate` keyword. Consider the following: > > var numbers = [5, 12, 6, 2] > mutate numbers.append(10) > mutate numbers.sort() > if let biggest = mutate numbers.popLast() { > print("The biggest number was:", biggest) > } > > So `mutate` would work much like `try` but—unlike `try` which can move > further to the left—`mutate` would have to always prefix the mutating > receiver. Here's a contrived example of a corner case: > > enum Error : ErrorType { case BadNumber } > > func demo() throws -> Int { > > } > >> * Are assignment methods a redundant way to spell mutating methods? >> Should we really have both mechanisms? > > (I had to look up the definition of an assignment method. For the > uninitiated, Dave is talking about what's written here: > https://github.com/apple/swift/blob/master/docs/proposals/Inplace.rst#use-one-simple-name > > <https://github.com/apple/swift/blob/master/docs/proposals/Inplace.rst#use-one-simple-name>.) > > — Yes they are redundant, and no, we should not have both. > > With `mutate` required at the call site, we could simply allow both overloads > `func sort()` and `mutating func sort()` to coexist, because the call sites > become unambiguous: > > let originals = [2, 1, 3, 0, 4, 2] > var copies = originals > > originals.sort() // warning: result of call to 'sort()' is > unused > mutate originals.sort() // compiler error > let xs = originals.sort() // ok > > copies.sort() // warning: result of call to 'sort()' is > unused > mutate copies.sort() // ok > let ys = copies.sort() // ok > let zs = mutate copies.sort() // warning: constant 'x' inferred to have > type '()', which may be unexpected > > The language could also allow the use of > > mutate x.next() > > as shorthand for > > x = x.next() > > when only the non-mutating variant `func next() -> Self` exists with > compatible return type. > >> * Can we really introduce this feature without having a way to apply it >> to class types? > > Yes we can. Why complicate the naming of value type members with the > complexities of reference semantics? The current API naming conventions are > good for reference types which sometimes come with unobvious to obscure > behaviour (i.e. anything from bumping an internal counter to firing missiles > and wiping hard drives). > > But value types ought to have no side effects (besides memory allocation and > logging maybe), and so we don't necessarily need that strong a naming > convention to limit their collateral damage. > > If the `mutate` keyword became required for calling `mutating` methods, then > operators would remain the only place where naming convention were needed to > distinguish mutation: > > Mutating assignment is explicit: `xs = [1, 2] + xs + [2, 1]` (i.e. `=` > without `let` or `var` means mutation) > Mutating method call becomes explicit: `mutate xs.sort()` and `let x = mutate > xs.removeAtIndex(2)` > Mutating function arguments are explicit with the `&` prefix: `swap(&xs, &ys)` > Mutating operators are implicit and by convention, should end with the `=` > symbol: `xs += [8, 9]` > Reference types have no notion of `mutating` members (and probably ought to > remain that way) so they mutate implicitly. > >> I should also point out that under the assignment method paradigm one >> would probably need to re-evalutate rules for naming. Under the current >> API guidelines' approach, we'd write: >> >> x.=sorted() // sort x in-place >> >> and I am not sure how easy that would be for people to swallow >> considering how much more straightforward >> >> x.sort() // current way to sort x in-place >> >> is, and because the language now contains explicit notation for >> mutation, it becomes harder to argue against theis pair: >> >> y = x.sort() >> x.=sort() // sort x in place > > I agree that the current API guidelines wouldn't work for value types > anymore. Both `sort` and `sorted` would be called `sort`. > >> Lastly, I should point out that the proposal does nothing to solve the >> problem of `c.formSuccessor(&i)`, since that doesn't mutate the >> receiver. > > This proposal does address the problem of `c.formSuccessor(&i)`. Given that > it's value types at play here, what mutates in the following is unambiguous > even to non-native English speakers: > > c.frobnicate(&i) // cannot possibly mutate c but mutates i > let j = c.frobnicate(i) // cannot possibly mutate either > mutate c.frobnicate(i) // mutates c > let k = mutate c.frobnicate(&i) // mutates both
How would this chain if I wanted to do something like: let median = foo.calculateBigHugeArray().sort().medianValue() and I want the sort to be done in place. Or will this type of thing just be disallowed in favor of. let array = foo.calculateBigHugeArray() mutate array.sort() let median = array.medianValue() Alternately you could replace the method invocation operator with & let median = foo.calculateBigHugeArray()&sort().medianValue() Or depending how sacrilegious you’re feeling you could use the only character on the keyboard that isn’t currently used and doesn’t require the shift key and is easily distinguished from a ‘.’ let median = foo.calculateBigHugeArray()'sort().medianValue() This is definitely more confusing than the & and mutate, since & is used to indicate mutation elsewhere. Also, if you wanted to stick with consistent & syntax, you could do: &c.frobnicate(i) and let k = &c.frobnicate(&i) Dave, to your point about classes, there is currently already special syntax for value types with the & as parameters. Reference semantics is the wild west there anyway. > >> I still like the proposal's basic approach and would love to see it used >> to address these naming problems, but I want to be clear that it's by no >> means a panacea and there are real obstacles between here and actually >> being able to apply it. If you want to move forward with something like >> this, you need to solve the problems described above. > > I think this proposal would simplify all code handling value types. Yes, it > adds one keyword of boilerplate but wins clarity in return. I think reference > types should stay separate from this discussion, as their mutation has always > been implicit anyway. The API guidelines set a good convention for them. > > — Pyry > > _______________________________________________ > 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
