[swift-evolution] [Pitch] Circling back to `with`

Matthew Johnson matthew at anandabits.com
Fri May 27 18:10:04 CDT 2016


> On May 27, 2016, at 5:12 PM, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org> wrote:
> 
>>> Just mentioning it as to end up... with the proper name for this new function.
>> 
>> Naming can always be bikeshedded. 
> 
> One possibility is to split `with` in two:

I much prefer this direction.

> 
> - A plain `with` whose closure parameter is not mutable and which is marked `@discardableResult`.

I would like to see this version restricted to AnyObject.  It has extremely limited utility with value types.  It would usually be a mistake to call it with a value type.

If we want a non-copying version that works with value types it should look like this:

@discardableResult
public func with<T>(_ item: inout T, use: @noescape (inout T) throws -> Void) rethrows {
    try use(&item)
}

That said, I am not convinced these non-copying functions would be worth having after method cascades are introduced.  Are there any use cases left for them in that future?

> 
> - A `withVar` whose parameter *is* mutable and which is *not* marked `@discardableResult`. (This would help with the fact that our use of `@discardableResult` is a little dangerous, in that people might expect mutations to affect the original variable even if it's a value type.)
> 
> `withVar` does, I think, make it pretty clear that you're working with a copy of the variable.

One thing to consider in choosing a name here is the cases where this function would still be useful in a future that includes method cascades.  The one thing this function does that method cascades don’t is make a copy of the value before operating on it and returning it.  

With that in mind, I think it is worthwhile to consider the name `withCopy` and make the closure argument optional.

public func withCopy<T>(_ item: T, update: (@noescape (inout T) throws -> Void)?) rethrows -> T {
    var this = item
    try update?(&this)
    return this
}

This function would be more clear and useful in conjunction with method cascades:

let bar = withCopy(foo)
    ..cascaded = “value"
    ..operations()
    ..onFoo()



> 
> 	/// Returns `item` after calling `use` to inspect it.
> 	/// 
> 	/// If `T` is a value type, `use` is unable to mutate `item`.
> 	/// If `T` is a reference type, `use` may use members which 
> 	/// change `item`, but cannot assign a different instance.
> 	@discardableResult
> 	public func with<T>(_ item: T, use: @noescape (T) throws -> Void) rethrows -> T {
> 	  try use(item)
> 	  return item
> 	}
> 	
> 	/// Returns `item` after calling `update` to inspect and possibly 
> 	/// modify it.
> 	/// 
> 	/// If `T` is a value type, `update` uses an independent copy 
> 	/// of `item`. If `T` is a reference type, `update` uses the 
> 	/// same instance passed in, but it can substitute a different 
> 	/// instance by setting its parameter to a new value.
> 	public func withVar<T>(_ item: T, update: @noescape (inout T) throws -> Void) rethrows -> T {
> 	  var this = item
> 	  try update(&this)
> 	  return this
> 	}
> 
> -- 
> Brent Royal-Gordon
> Architechies
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list