[swift-evolution] mutating/non-mutating suggestion from a Rubyist

Pyry Jahkola pyry.jahkola at iki.fi
Sat Apr 23 03:27:10 CDT 2016


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 at 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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160423/b05dd86d/attachment.html>


More information about the swift-evolution mailing list