[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