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

Pyry Jahkola pyry.jahkola at iki.fi
Thu Apr 28 02:34:32 CDT 2016


Good that you brought the topic of "fluent" interfaces up. I don't see any problem with explicit value type mutation and method chaining because fluent interfaces are constrained to reference types by the language. Details below:

> On 28 Apr 2016, at 03:44, Tyler Cloutier <cloutiertyler at aol.com> wrote:
> 
> 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.

I think I can guess what you wanted the above to mean but, mind you, the in-place sort returns `()` so you wouldn't chain its result like that. On the other hand, the above code already works using the non-mutating `.sort()` (to be known as `.sorted()` in Swift 3), and—correct me if I'm wrong—the compiler probably optimises the copy away using copy-on-write anyway.

> Or will this type of thing just be disallowed in favor of.
> 
> let array = foo.calculateBigHugeArray()
> mutate array.sort()
> let median = array.medianValue() 

Yes, I think mutating code should be written in many statements rather than squeezing everything into one long expression.

Indeed, no currently working method chaining would be disallowed in my proposal. Let's consider the example of "fluent API" for value types, i.e. one where you'd extensively `return self` in `mutating` methods. Firstly, the stdlib doesn't practice that at all. And I failed to find any popular Swift libraries that would do so on Github either. The reason is simple: fluent mutating APIs on value types don't work.

Consider the following silly example that shortens an array in half (but demonstrates the use of `return self` in a `mutating` method):

    extension Array {
        mutating func halve() -> Array {
            self = self[0 ..< count / 2]
            return self
        }
    }

Suppose I want to get the result of halving an array twice. What happens?

    var xs = [1,2,3,4,5,6,7,8]
    xs.halve().halve()
    // error: cannot use mutating member on immutable value: function call returns immutable value

So no, fluent APIs on value types are not a thing in Swift. Not now at least. Making mutation explicit along this proposal has nothing to do with fluent APIs.

> Alternately you could replace the method invocation operator with &
> 
> let median = foo.calculateBigHugeArray()&sort().medianValue()

Don't you think that's too prone to getting mixed up with the binary `&` operator?

> Also, if you wanted to stick with consistent & syntax, you could do:
> 
> &c.frobnicate(i)
> and 
> let k = &c.frobnicate(&i)


Yeah, probably. However, one place where that notation falls short compared to a prefixing keyword like `mutate` is when mutating `self`:

    extension Array {
        // Apologies for not having the time to think of a less contrived example than this!
        mutating func quarter() {
            mutate self.halve() // Ever since SE-0009 <https://github.com/apple/swift-evolution/blob/master/proposals/0009-require-self-for-accessing-instance-members.md>, it's unusual to use `self` here.
            mutate halve()      // Where would you put the `&` prefix in this?
        }
    }

— Pyry

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


More information about the swift-evolution mailing list