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 
> <swift-evolution@swift.org> 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

> 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to