Proposal: Add replace(_:with:) function to the stdlib


(Lily Ballard) #1

A function I find myself defining in a lot of my projects looks like the following:

/// Replace the value of `a` with `b` and return the old value. public
func replace<T>(inout a: T, with b: T) -> T { var value = b
swap(&a, &value) return value }

This is a pretty simple function, and useful in a wide variety of
circumstances, so I'd love to get it into the standard library. It
doesn't actually enable any behavior that wasn't previously possible,
but it does shrink some common code patterns, and I find the shorter
code easier to read.

An example of a place where I use it often is in replacing an optional
property with a new value (or with nil) and cleaning up the previous
value. Assuming a property like

var task: NSURLSessionTask?

This replaces

if let task = self.task { task.cancel() } task = nil

with

replace(&task, with: nil)?.cancel()

Or sometimes I use it like

if let value = replace(&prop, with: newValue) { // multi-line cleanup
}

This is particularly nice if it's a COW value that I want to mutate, as
it means I don't have to worry about getting unwanted copies due to the
property still holding the old value while I muck with it.

Question: For trivial backwards-compatible API changes like this, does a
proposal PR need to be submitted to the swift-evolution repo, or is
discussion on this ML sufficient before submitting a patch?

-Kevin Ballard


(Dave Abrahams) #2

A function I find myself defining in a lot of my projects looks like the following:

/// Replace the value of `a` with `b` and return the old value.
public func replace<T>(inout a: T, with b: T) -> T {
    var value = b
    swap(&a, &value)
    return value
}

This is a pretty simple function, and useful in a wide variety of circumstances, so I'd love to get it into the standard library. It doesn't actually enable any behavior that wasn't previously possible, but it does shrink some common code patterns, and I find the shorter code easier to read.

An example of a place where I use it often is in replacing an optional property with a new value (or with nil) and cleaning up the previous value. Assuming a property like

var task: NSURLSessionTask?

This replaces

if let task = self.task {
    task.cancel()
}
task = nil

with

replace(&task, with: nil)?.cancel()

Or sometimes I use it like

if let value = replace(&prop, with: newValue) {
    // multi-line cleanup
}

This is particularly nice if it's a COW value that I want to mutate, as it means I don't have to worry about getting unwanted copies due to the property still holding the old value while I muck with it.

This is a generalization of the postincrement pattern (mutate a value and return the original) and given that we're moving away from that I'm not sure it's something we want to enshrine in the standard library. That said, here's a question: looking at your use cases, how many of them are using something other than nil (or some moral equivalent) as the second argument? If this is effectively a non-destructive move in nearly all cases, I'd rather support that more directly.

If cases other than "nondestructive move" are common enough, I'd consider something like this syntax instead:

  (task <- nil).cancel()

But I strongly suspect this isn't a common enough pattern to warrant introducing an operator.

Question: For trivial backwards-compatible API changes like this, does a proposal PR need to be submitted to the swift-evolution repo, or is discussion on this ML sufficient before submitting a patch?

A proposal is needed for all new/changed API.

-Dave

···

On Dec 13, 2015, at 2:21 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:


(Paul Cantrell) #3

Nice idiom! Even if it doesn’t make it into stdlib, I might have to use that in some project-local utilities. Quite slick.

Is there a way to make it read more grammatically?

    replace(&task, with: nil).andWithPreviousValue?.cancel()

Yuck. I don’t see a way that works well … but you see what I’m getting at.

P

···

On Dec 13, 2015, at 4:21 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

A function I find myself defining in a lot of my projects looks like the following:

/// Replace the value of `a` with `b` and return the old value.
public func replace<T>(inout a: T, with b: T) -> T {
    var value = b
    swap(&a, &value)
    return value
}

This is a pretty simple function, and useful in a wide variety of circumstances, so I'd love to get it into the standard library. It doesn't actually enable any behavior that wasn't previously possible, but it does shrink some common code patterns, and I find the shorter code easier to read.

An example of a place where I use it often is in replacing an optional property with a new value (or with nil) and cleaning up the previous value. Assuming a property like

var task: NSURLSessionTask?

This replaces

if let task = self.task {
    task.cancel()
}
task = nil

with

replace(&task, with: nil)?.cancel()

Or sometimes I use it like

if let value = replace(&prop, with: newValue) {
    // multi-line cleanup
}

This is particularly nice if it's a COW value that I want to mutate, as it means I don't have to worry about getting unwanted copies due to the property still holding the old value while I muck with it.

Question: For trivial backwards-compatible API changes like this, does a proposal PR need to be submitted to the swift-evolution repo, or is discussion on this ML sufficient before submitting a patch?

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #4

Proposal PR submitted as https://github.com/apple/swift-evolution/pull/93

-Kevin Ballard

···

On Sun, Dec 13, 2015, at 02:21 PM, Kevin Ballard wrote:

A function I find myself defining in a lot of my projects looks like
the following:

/// Replace the value of `a` with `b` and return the old value. public
func replace<T>(inout a: T, with b: T) -> T { var value = b
swap(&a, &value) return value }

This is a pretty simple function, and useful in a wide variety of
circumstances, so I'd love to get it into the standard library. It
doesn't actually enable any behavior that wasn't previously possible,
but it does shrink some common code patterns, and I find the shorter
code easier to read.

An example of a place where I use it often is in replacing an optional
property with a new value (or with nil) and cleaning up the previous
value. Assuming a property like

var task: NSURLSessionTask?

This replaces

if let task = self.task { task.cancel() } task = nil

with

replace(&task, with: nil)?.cancel()

Or sometimes I use it like

if let value = replace(&prop, with: newValue) { // multi-line
cleanup }

This is particularly nice if it's a COW value that I want to mutate,
as it means I don't have to worry about getting unwanted copies due to
the property still holding the old value while I muck with it.

Question: For trivial backwards-compatible API changes like this, does
a proposal PR need to be submitted to the swift-evolution repo, or is
discussion on this ML sufficient before submitting a patch?

-Kevin Ballard


(ilya) #5

That's a useful pattern, but the implementation above creates a function
that has both a return value and a side effect, which is likely to be
unexpected for many readers.

public func replace<T>(inout a: T?, with b: T?, andCleanOldValue
clean:T->()) { ... }

replace(&prop, with: newValue) { oldValue in
    oldValue.clean()
}

As a bonus one can automatically unwrap nil.

Although in real life I tend to move this logic to property accessors, e.g.

// Currently executing task. This property manages task state.

var task: NSURLSessionTask? {
  willSet { task?.cancel() }
  didSet { task?.resume() }
}

···

On Mon, Dec 14, 2015 at 7:59 AM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

On Dec 13, 2015, at 2:21 PM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:

A function I find myself defining in a lot of my projects looks like the
following:

/// Replace the value of `a` with `b` and return the old value.
public func replace<T>(inout a: T, with b: T) -> T {
    var value = b
    swap(&a, &value)
    return value
}

This is a pretty simple function, and useful in a wide variety of
circumstances, so I'd love to get it into the standard library. It doesn't
actually enable any behavior that wasn't previously possible, but it does
shrink some common code patterns, and I find the shorter code easier to
read.

An example of a place where I use it often is in replacing an optional
property with a new value (or with nil) and cleaning up the previous value.
Assuming a property like

var task: NSURLSessionTask?

This replaces

if let task = self.task {
    task.cancel()
}
task = nil

with

replace(&task, with: nil)?.cancel()

Or sometimes I use it like

if let value = replace(&prop, with: newValue) {
    // multi-line cleanup
}

This is particularly nice if it's a COW value that I want to mutate, as it
means I don't have to worry about getting unwanted copies due to the
property still holding the old value while I muck with it.

This is a generalization of the postincrement pattern (mutate a value and
return the original) and given that we're moving away from that I'm not
sure it's something we want to enshrine in the standard library. That said,
here's a question: looking at your use cases, how many of them are using
something other than nil (or some moral equivalent) as the second
argument? If this is effectively a non-destructive move in nearly all
cases, I'd rather support that more directly.

If cases other than "nondestructive move" are common *enough*, I'd
consider something like this syntax instead:

  (task <- nil).cancel()

But I strongly suspect this isn't a common enough pattern to warrant
introducing an operator.

Question: For trivial backwards-compatible API changes like this, does a
proposal PR need to be submitted to the swift-evolution repo, or is
discussion on this ML sufficient before submitting a patch?

A proposal is needed for all new/changed API.

-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #6

This is a generalization of the postincrement pattern (mutate a value
and return the original) and given that we're moving away from that
I'm not sure it's something we want to enshrine in the standard
library.

It's also a generalization of Dictionary.updateValue(_:forKey:) and
Dictionary.removeValueForKey(_:), and those aren't going away. Actually
I'd say it's a lot more like the Dictionary methods and less like
postincrement because it's not actually mutating the original value,
it's simply replacing it with a new value.

That said, here's a question: looking at your use cases, how many of
them are using something other than nil (or some moral equivalent) as
the second argument? If this is effectively a non-destructive move in
nearly all cases, I'd rather support that more directly.

In my current codebase, I have 21 uses of replace(&foo, with: nil) and 1
use of replace(&foo, with: bar). That said, in my last big Swift project
at my previous job I'm pretty sure I had more uses of replace(&foo,
with: bar) (but I don't have the source anymore to check).

If cases other than "nondestructive move" are common _enough_, I'd
consider something like this syntax instead:

(task <- nil).cancel()

But I strongly suspect this isn't a common enough pattern to warrant
introducing an operator.

Interesting idea. I'm inclined to agree that this isn't common enough to
introduce an operator for.

-Kevin Ballard

···

On Sun, Dec 13, 2015, at 08:59 PM, Dave Abrahams wrote:


(Zhao Xin) #7

what is the code

*var a = 1*
*var b = -1*
*var c = replace(&a, with: b*)

different from

*var a = 1*
*var b = -1*
*var c = a*
*a = b*

?

For me, the latter is more clearer.

···

On Sun, Jan 10, 2016 at 8:48 AM, Kevin Ballard via swift-evolution < swift-evolution@swift.org> wrote:

Proposal PR submitted as https://github.com/apple/swift-evolution/pull/93

-Kevin Ballard

On Sun, Dec 13, 2015, at 02:21 PM, Kevin Ballard wrote:

A function I find myself defining in a lot of my projects looks like the
following:

/// Replace the value of `a` with `b` and return the old value.
public func replace<T>(inout a: T, with b: T) -> T {
    var value = b
    swap(&a, &value)
    return value
}

This is a pretty simple function, and useful in a wide variety of
circumstances, so I'd love to get it into the standard library. It doesn't
actually enable any behavior that wasn't previously possible, but it does
shrink some common code patterns, and I find the shorter code easier to
read.

An example of a place where I use it often is in replacing an optional
property with a new value (or with nil) and cleaning up the previous value.
Assuming a property like

var task: NSURLSessionTask?

This replaces

if let task = self.task {
    task.cancel()
}
task = nil

with

replace(&task, with: nil)?.cancel()

Or sometimes I use it like

if let value = replace(&prop, with: newValue) {
    // multi-line cleanup
}

This is particularly nice if it's a COW value that I want to mutate, as it
means I don't have to worry about getting unwanted copies due to the
property still holding the old value while I muck with it.

Question: For trivial backwards-compatible API changes like this, does a
proposal PR need to be submitted to the swift-evolution repo, or is
discussion on this ML sufficient before submitting a patch?

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--

Owen Zhao


(Brent Royal-Gordon) #8

One thing I noticed is that this appears to be a generalization of `Dictionary.updateValue(_:forKey:)`. Should the dictionary-specific version continue to exist? If it should, should we modify one or the other to have more closely-matching naming or argument ordering?

···

--
Brent Royal-Gordon
Architechies


(Jordan Rose) #9

-1 from me as well, for the same reason as Dave. In the Dictionary case, doing a lookup and then a replacement isn't just more verbose; it's actually less efficient. And I still wouldn't embed an updateValue(_:forKey:) in a larger expression, for clarity's sake.

Now, admittedly, if the assigned-to value were some complicated expression (e.g. "view.frame.size.height"), the same performance argument applies. But I still think I'd rather people just use a temporary variable.

self.task?.cancel()
self.task = nil

let oldValue = self.prop
self.prop = newValue
if let oldValue = oldValue {
  // multi-line cleanup
}

I'll admit the latter is not as nice, but it's definitely easier for me to read.

Jordan

···

On Jan 9, 2016, at 16:48, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

Proposal PR submitted as https://github.com/apple/swift-evolution/pull/93

-Kevin Ballard

On Sun, Dec 13, 2015, at 02:21 PM, Kevin Ballard wrote:

A function I find myself defining in a lot of my projects looks like the following:

/// Replace the value of `a` with `b` and return the old value.
public func replace<T>(inout a: T, with b: T) -> T {
    var value = b
    swap(&a, &value)
    return value
}

This is a pretty simple function, and useful in a wide variety of circumstances, so I'd love to get it into the standard library. It doesn't actually enable any behavior that wasn't previously possible, but it does shrink some common code patterns, and I find the shorter code easier to read.

An example of a place where I use it often is in replacing an optional property with a new value (or with nil) and cleaning up the previous value. Assuming a property like

var task: NSURLSessionTask?

This replaces

if let task = self.task {
    task.cancel()
}
task = nil

with

replace(&task, with: nil)?.cancel()

Or sometimes I use it like

if let value = replace(&prop, with: newValue) {
    // multi-line cleanup
}

This is particularly nice if it's a COW value that I want to mutate, as it means I don't have to worry about getting unwanted copies due to the property still holding the old value while I muck with it.

Question: For trivial backwards-compatible API changes like this, does a proposal PR need to be submitted to the swift-evolution repo, or is discussion on this ML sufficient before submitting a patch?

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #10

Your versions throw away the whole point of the function, which is to make common patterns easier, not more awkward. Adding an unnecessary scope there just makes it impossible to use the function in otherwise-valid circumstances. You're also focusing on the one example I gave, which is tearing down an old value (which cannot be done in property observers if it should only happen sometimes).

I'll grant you that it's uncommon to have functions with both side-effects and return values, but it's certainly not unheard-of. Heck, that's how Generators work!

-Kevin Ballard

···

On Dec 13, 2015, 11:36 PM -0800, ilya<ilya.nikokoshev@gmail.com>, wrote:

That's a useful pattern, but the implementation above creates a function that has both a return value and a side effect, which is likely to be unexpected for many readers.

public func replace<T>(inout a: T?, with b: T?, andCleanOldValue clean:T->()) { ... }

replace(&prop, with: newValue) { oldValue in
oldValue.clean()
}

As a bonus one can automatically unwrap nil.

Although in real life I tend to move this logic to property accessors, e.g.

// Currently executing task. This property manages task state.

var task: NSURLSessionTask? {
willSet { task?.cancel() }
didSet { task?.resume() }
}

On Mon, Dec 14, 2015 at 7:59 AM, Dave Abrahams via swift-evolution<swift-evolution@swift.org(mailto:swift-evolution@swift.org)>wrote:
>
> > On Dec 13, 2015, at 2:21 PM, Kevin Ballard via swift-evolution<swift-evolution@swift.org(mailto:swift-evolution@swift.org)>wrote:
> > A function I find myself defining in a lot of my projects looks like the following:
> > /// Replace the value of `a` with `b` and return the old value.
> > public func replace<T>(inout a: T, with b: T) ->T {
> > var value = b
> > swap(&a,&value)
> > return value
> > }
> > This is a pretty simple function, and useful in a wide variety of circumstances, so I'd love to get it into the standard library. It doesn't actually enable any behavior that wasn't previously possible, but it does shrink some common code patterns, and I find the shorter code easier to read.
> > An example of a place where I use it often is in replacing an optional property with a new value (or with nil) and cleaning up the previous value. Assuming a property like
> > var task: NSURLSessionTask?
> > This replaces
> > if let task = self.task {
> > task.cancel()
> > }
> > task = nil
> > with
> > replace(&task, with: nil)?.cancel()
> > Or sometimes I use it like
> > if let value = replace(&prop, with: newValue) {
> > // multi-line cleanup
> > }
> > This is particularly nice if it's a COW value that I want to mutate, as it means I don't have to worry about getting unwanted copies due to the property still holding the old value while I muck with it.
>
> This is a generalization of the postincrement pattern (mutate a value and return the original) and given that we're moving away from that I'm not sure it's something we want to enshrine in the standard library. That said, here's a question: looking at your use cases, how many of them are using something other than nil (or some moral equivalent) as the second argument?If this is effectively a non-destructive move in nearly all cases, I'd rather support that more directly.
>
> If cases other than "nondestructive move" are commonenough, I'd consider something like this syntax instead:
>
> (task<- nil).cancel()
>
> But I strongly suspect this isn't a common enough pattern to warrant introducing an operator.
>
> > Question: For trivial backwards-compatible API changes like this, does a proposal PR need to be submitted to the swift-evolution repo, or is discussion on this ML sufficient before submitting a patch?
> A proposal is needed for all new/changed API.
>
> -Dave
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org(mailto:swift-evolution@swift.org)
> https://lists.swift.org/mailman/listinfo/swift-evolution
>


(Martin Kühl) #11

It’s also how all mutating funcs on Dictionary behave.

-Martin

···

On 14 December 2015 at 09:30, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

I'll grant you that it's uncommon to have functions with both side-effects and return values, but it's certainly not unheard-of. Heck, that's how Generators work!


(Lily Ballard) #12

Neither code snippet appears to be doing much at all. It's hard to judge
anything at all when the code has been reduced that far.

-Kevin Ballard

what is the code

*var a = 1* *var b = -1* *var c = replace(&a, with: b*)

different from

*var a = 1* *var b = -1* *var c = a* *a = b*

?

For me, the latter is more clearer.

__
Proposal PR submitted as
https://github.com/apple/swift-evolution/pull/93

-Kevin Ballard

A function I find myself defining in a lot of my projects looks like
the following:

/// Replace the value of `a` with `b` and return the old value.
public func replace<T>(inout a: T, with b: T) -> T { var value =
b swap(&a, &value) return value }

This is a pretty simple function, and useful in a wide variety of
circumstances, so I'd love to get it into the standard library. It
doesn't actually enable any behavior that wasn't previously
possible, but it does shrink some common code patterns, and I find
the shorter code easier to read.

An example of a place where I use it often is in replacing an
optional property with a new value (or with nil) and cleaning up the
previous value. Assuming a property like

var task: NSURLSessionTask?

This replaces

if let task = self.task { task.cancel() } task = nil

with

replace(&task, with: nil)?.cancel()

Or sometimes I use it like

if let value = replace(&prop, with: newValue) { // multi-line
cleanup }

This is particularly nice if it's a COW value that I want to mutate,
as it means I don't have to worry about getting unwanted copies due
to the property still holding the old value while I muck with it.

Question: For trivial backwards-compatible API changes like this,
does a proposal PR need to be submitted to the swift-evolution repo,
or is discussion on this ML sufficient before submitting a patch?

-Kevin Ballard

_______________________________________________

swift-evolution mailing list

···

On Sat, Jan 9, 2016, at 09:53 PM, 肇鑫 wrote:

On Sun, Jan 10, 2016 at 8:48 AM, Kevin Ballard via swift-evolution <swift- > evolution@swift.org> wrote:

On Sun, Dec 13, 2015, at 02:21 PM, Kevin Ballard wrote:
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--

Owen Zhao


(Lily Ballard) #13

Good question. I'm inclined to say that we should keep Dictionary.updateValue(_:forKey:) for two reasons:

1. Documentation. It's reasonable to expect a function like Dictionary.updateValue(_:forKey:) to be found on Dictionary.
2. Performance. Dictionary's getter/setter combo is implemented using a classic computed getter/setter pair (as opposed to mutableAddressWithPinnedNativeOwner), which makes sense because adding or deleting keys need to do more than manipulate the contents of an address. But because of this, using an inout expression &d[key] actually invokes the getter, performs the mutations, and then invokes the setter with the new value. This means that the expression `replace(&d[key], with: newValue)` actually looks up the key in the hashtable twice, whereas Dictionary.updateValue(_:forKey:) only has to look it up once.

I don't think it makes sense to swap the arguments for replace(_:with:). We could change updateValue(_:forKey:) to instead be called something like updateKey(_:withValue:), but the current name updateValue(_:forKey:) is consistent with the method removeValueForKey(_:).

So ultimately, while I recognize that `replace(&d[key], with: newValue)` is equivalent to `d.updateValue(newValue, forKey: key)` (though likely with slightly worse performance), I think we should keep both replace(_:with:) and Dictionary.updateValue(_:forKey:) as-is.

-Kevin Ballard

···

On Sun, Jan 10, 2016, at 04:30 PM, Brent Royal-Gordon wrote:

One thing I noticed is that this appears to be a generalization of `Dictionary.updateValue(_:forKey:)`. Should the dictionary-specific version continue to exist? If it should, should we modify one or the other to have more closely-matching naming or argument ordering?


(Lily Ballard) #14

FWIW, in the code I've written over the past few days in a new project, I've already used my local replace(_:with:) 6 times, and I just tried to use it a 7th from within the unit tests and got an error because the function is private >_<

It does turn out that nearly all of my particular uses for this function
end up replacing the value with `nil`, but there certainly are cases
where you'd want to replace a value with some other non-nil value (such
as the example I listed in the proposal about a spot where it might be
used in the stdlib).

-1 from me as well, for the same reason as Dave. In the Dictionary
case, doing a lookup and then a replacement isn't just more verbose;
it's actually less efficient. And I *still* wouldn't embed an
updateValue(_:forKey:) in a larger expression, for clarity's sake.

I'm not recommending that replace(_:with:) be used as a replacement for
Dictionary.updateValue(_:forKey:). I just found it interesting that it
has the exact same semantics (but, as you said, it's less performant).

Now, admittedly, if the assigned-to value were some complicated
expression (e.g. "view.frame.size.height"), the same performance
argument applies. But I still think I'd rather people just use a
temporary variable.

What do you mean? Using `replace(&view.frame.size.height, with:
newHeight)` is no less performant than doing things manually. The
Dictionary.updateValue(_:forKey:) case only differs because
accessing the subscript getter + setter has to look up the key
twice, but `&view.frame.size.height` doesn't have to look up
anything, it just has to read the property and then set it again,
which is exactly what you have to do if you want to read the old
value and assign a new one regardless of whether you're using a
function like replace(_:with:) to do it.

self.task?.cancel() self.task = nil

let oldValue = self.prop self.prop = newValue if let oldValue =
oldValue { // multi-line cleanup }

I'll admit the latter is not as nice, but it's definitely easier for
me to read.

Maybe for you. I find it much easier to read

if let oldValue = replace(&self.prop, with: nil) {

// ...

}

I recognize that many people will prefer the "manual" version, but I'm
sure many people will be like me and prefer the replace(_:with:)
version. I don't find the argument "I think it's easier to read <some
other way>" to be very compelling when it comes to stdlib or language
features, because we already have plenty of precedent for being able to
write the same thing in multiple ways. There's also a middle ground
here, if you just don't like embedding replace(_:with:) in a larger
construct, that still uses a temporary variable but allows you to avoid
duplicating complicated accessor expressions, e.g.

let oldValue =
replace(&self.sections[indexPath.section].rows[indexPath.row].data,
with: newValue)

if let oldValue = oldValue {

// ...

}

I've also found a lot of use for replace(_:with:) in `guard let`
expressions. There especially the "manual" version sucks because it
leaves a second variable lying around that you don't want anymore. In
the library I'm working on right now, I have a bunch of uses of
replace(_:with:) that look like

guard var request = replace(&self.request, with; nil) else {

writeResponse(Response(status: .InternalServerError, text: "Internal
server error: couldn't find active request"))

return

}

(yes, this is actually for an HTTP server written in Swift, which I'm
using to test networking code)

What's your opinion on the alternative <- operator (which IIRC Dave
suggested)? I much prefer replace(_:with:), and I don't think it
conflicts with stdlib conventions (it's no different than a mutating
method that returns a value, it just happens to be a function, but
there's not much of a difference between mutating methods and functions
that take an inout first parameter), but I'd rather have this
functionality as an <- operator than not have it at all. As an example,
the previous two code samples, when written with <-, would look like

let oldValue =
(self.sections[indexPath.section].rows[indexPath.row].data <- newValue)

if let oldValue = oldValue {

// ...

}

and

guard var request = (self.request <- nil) else {

writeResponse(Response(status: .InternalServerError, text: "Internal
server error: couldn't find active request"))

return

}

-Kevin Ballard

···

On Mon, Jan 11, 2016, at 01:22 PM, Jordan Rose wrote:

Jordan

On Jan 9, 2016, at 16:48, Kevin Ballard via swift-evolution <swift- >> evolution@swift.org> wrote:

Proposal PR submitted as
https://github.com/apple/swift-evolution/pull/93

-Kevin Ballard

On Sun, Dec 13, 2015, at 02:21 PM, Kevin Ballard wrote:

A function I find myself defining in a lot of my projects looks like
the following:

/// Replace the value of `a` with `b` and return the old value.
public func replace<T>(inout a: T, with b: T) -> T { var value =
b swap(&a, &value) return value }

This is a pretty simple function, and useful in a wide variety of
circumstances, so I'd love to get it into the standard library. It
doesn't actually enable any behavior that wasn't previously
possible, but it does shrink some common code patterns, and I find
the shorter code easier to read.

An example of a place where I use it often is in replacing an
optional property with a new value (or with nil) and cleaning up the
previous value. Assuming a property like

var task: NSURLSessionTask?

This replaces

if let task = self.task { task.cancel() } task = nil

with

replace(&task, with: nil)?.cancel()

Or sometimes I use it like

if let value = replace(&prop, with: newValue) { // multi-line
cleanup }

This is particularly nice if it's a COW value that I want to mutate,
as it means I don't have to worry about getting unwanted copies due
to the property still holding the old value while I muck with it.

Question: For trivial backwards-compatible API changes like this,
does a proposal PR need to be submitted to the swift-evolution repo,
or is discussion on this ML sufficient before submitting a patch?

-Kevin Ballard

_______________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution