On 28 April 2016 at 10:44, Tyler Cloutier via swift-evolution < swift-evolution@swift.org> wrote:
On Apr 23, 2016, at 1:27 AM, Pyry Jahkola via swift-evolution < > swift-evolution@swift.org> 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 < > 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>\.
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
.)
— 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution