But also, even if an ideal Swift API would not require these things (which I'm not sure about) the fact is there are a lot of types where this kind of copy + mutation is beneficial.
I feel like the with name is already used in the Swift community and it does kind of make sense. "With" a copy of the value, perform changes". You could argue that it would make more sense for it to mean "With" the original value, do something, but for value types that's almost completely useless (and barely easier than just writing a closure and calling it immediately).
And while there are many reference types that could just be modified like this:
IMO the code above is somewhat ugly and difficult to read (I agree with you there) and there should be a better way to do this! With with you can write it like this:
Which is more readable for various reasons. Also unlike forEach I don't see any control flow that would be confusing in a with closure other than returning, maybe? Even then the compiler would throw an error since you can't return in a Void function.
Since the beginning of Swift, I think a lot of people have defaulted to let on properties when they should really prefer var. Probably a consequence of âimmutable data canât have racesâ and defaulting to let for local bindings.
One problem I have is that your example does in fact look to me like itâs mutating the reference type. And that is actually a useful operation, especially when trying to mutate something stored behind a long path of accessors.
var ref = ReferenceType()
var copy = ref
copy.foo = bar
This is just how reference types work: modifying a copy will modify the original, so it is a copy-and mutate operation. It's just that copying a reference type just returns, well, a reference!
I donât think weâd want with to be @discardableResult. That would encourage using it for its side-effects instead of as a pure âcopy and modifyâ function, and would lose the valuable âunused resultâ warning above.
One doesn't simply put all imaginable semantics into a single function. You either "copy, mutate, return" or "borrow, mutate" or "consume, mutate, return" or "borrow, transform, return" or "consume, transform, return".
Ben, to what degree would you feel better about this if it were a free function instead of a magic method? Weâd lose the nice interaction with optional chaining, but on the other hand, it would be clearer that it wasnât a mutation of the original value, and it might make available some more suggestive names.
I feel like this has gone full circle: In the original pitch thread the name was debated a lot and whether it should be an operator, free function or method was also, and it seemed like people couldn't agree on a better name and thought that it should be a method (at least in the poll I did)
Well, Iâm not saying thatâs the way to go; but even if we do end up back there, I think weâve definitely explored the issues far better than we had before, which is the purpose of a pitch.
The second accesses myModel.items[selectedRowIndex] twice and makes an unnecessary copy. Is the first pattern really so much more common than the second?
This is very very different semantic. It's "mutate inplace", not "make a copy, mutate, and return". These two very distinct functions shouldn't share a name, whatever it will be.
We are not talking here about a function here that can mutate an l-value in-place. That would absolutely need to spelled differently from the (copy)-mutate-return operation. But if we did want to add such a function as a free function, it could simply take an inout argument, again modulo the interaction with optional chaining.
But with is a valid name for both operations, and in other languages such as Groovy and Kotlin it does mean in-place mutation. Is the prevalence of value types in Swift sufficient to explain this difference in semantics?