[swift-evolution] ed/ing, InPlace, Set/SetAlgebra naming resolution

Greg Parker gparker at apple.com
Fri Feb 12 01:26:27 CST 2016

> On Feb 11, 2016, at 10:05 PM, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
>> on Thu Feb 11 2016, Xiaodi Wu <swift-evolution at swift.org> wrote:
>> Understandable. FWIW, if one believes in autocomplete, superscript
>> equal sign is already a valid identifier head character (per
>> documentation and experimentation in a playground). So, if the gist of
>> the proposal is acceptable, one can already name a pair of functions
>> union() and union=() if the "=" is replaced with its superscript (and
>> for that matter, =union(), but autocomplete might not help with that
>> one). 
> Hmm, use the fullwidth equal sign; it reads better, and compiles:
>  mutating func =union(other: Self) { ... }
> Heh, that is awesome; we could do some real fun proofs-of-concept for
> the proposal using that trick.  The autocomplete problem can/should be
> fixed by tooling.

Now y'all are approaching the syntax of a proposal I made long ago for a .= operator. Here's what I wrote then (with a handful of syntax updates).


I suggest eliminating "…InPlace" as follows:
1. Use the same method name for value-creating methods and in-place mutation methods. No "…InPlace" suffix.
2. Write all client code syntactically as if the in-place implementations did not exist. Rely on the compiler to choose the in-place implementation when possible.
3. Add a new operator .= for in-place modification, to shorten the "expr = expr.method()" case.

1. Use the same method name for value-creating methods and in-place mutation methods

struct String {
   func upper() -> String { ... }  // value-creating / out-of-place implementation
   @inplace func upper() { ... }   // in-place implementation

struct String2 {
   @inplace func upper() { ... }   // in-place implementation

The value-creating implementation is optional. If it is not present, the compiler will generate a default value-creating implementation that copies the object and calls the in-place implementation. There is no default in-place implementation; if there is no in-place implementation of a value-creating method then the compiler simply fails to optimize to it.

2. Write all client code syntactically as if the in-place implementations did not exist.

Instead of this
write this
   s = s.upper()

The compiler optimizer can choose the in-place upper() implementation if it is present.

Instead of this 
   t = s.upper().trim().truncate(toLength: 5)                // oops, there's an extra copy here
   t = s.upper().trimInPlace().truncateInPlace(toLength: 5)  // oops, compile error
write this
   t = s.upper().trim().truncate(toLength: 5)

The developer can chain methods together and let the compiler optimizer choose which in-place implementations to use. In this case, if all in-place implementations are available, it should call value-creating upper() followed by in-place trim() and in-place truncate(toLength:).

3. Add operator .= for in-place modification.

Operator .= is analogous to arithmetic operators like += . It is shorthand for `expr = expr.stuff` for the case where `expr` is inconveniently long. (It also doesn't evaluate `expr` twice, if Swift's optimization rules otherwise would allow it or require it for the longhand case.)

s .= upper()  
// like s = s.upper()

p .= next
// like p = p.next

some().long().expression .= upper().trim().truncateToLength(5)
// like some().long().expression = some().long().expression.upper().trim().truncateToLength(5)

As seen in this last example, one advantage of this syntax is that it allows chained mutations but cleanly separates the "lookup" calls from the "mutate" calls. This is an improvement upon language and API designs with mutating methods where the property being changed is buried in the middle of the expression somewhere.

some().long().expression.upperInPlace().trimInPlace().truncateInPlace(toLength: 5)
some().long().expression().upper.trimInPlace().truncateInPlace(toLength: 5)

some().long().expression .= upper().trim().truncate(toLength: 5)
some().long().expression().upper .= trim().truncate(toLength: 5)

One consequence of these changes is that compiler optimizations to take advantage of in-place implementation are much more important for performance, because there isn't any syntax to call the in-place implementation directly.


Greg Parker     gparker at apple.com     Runtime Wrangler

More information about the swift-evolution mailing list