Circling back to `with`

Over the past couple of days, the Twitters have discovered some work I'd done on closure-based setup.
It's clear that a demand is out there and strong for this kind of behavior, even without implicit `self` as
part of the mix or cascading. In that light, I've put together the following:

If the community demand is this high, I think we should re-consider pushing it before 3.
Feedback as always welcome, including criticism.

-- E

Introducing with to the Standard Library

Proposal: TBD
Author: Erica Sadun <https://github.com/erica&gt;
Status: TBD
Review manager: TBD
<with.md · GitHub

This proposal introduces a with function to the standard library to simplify the initialization and modification of constants and Foundation-sourced complex objects.

Swift-evolution thread: What about a VBA style with Statement? <http://thread.gmane.org/gmane.comp.lang.swift.evolution/14384&gt;
<with.md · GitHub

Closure-based initialization enables clean and highly directed set-up in Swift code. Numerous variations on the theme have been introduced on the Swift Evolution list and in third party github repositories. Although you can build solutions natively without functions, current Swift technology has drawbacks:

let questionLabel: UILabel = {
    $0.textAlignment = .Center
    $0.font = UIFont(name:"DnealianManuscript", size: 72)
    $0.text = questionText
    $0.numberOfLines = 0
    return $0
}(UILabel())

let mySwitch : UISwitch = {
    view.addSubview($0)
    CenterViewInSuperview($0, horizontal: true, vertical: true)
    $0.addTarget(self, action: "action", forControlEvents: .TouchUpInside)
    return $0
}(UISwitch())
Assignment must be explicitly typed.
The source item must be postpended to the set-up closure.
The closure must return the item.
This approach is better suited to setting up Foundation objects than modifying Swift constants. When duplicating and modifying a constant, the closure must create a var copy and modify that copy.
While the implementation is imperfect, the wins are notable. Code naturally groups into a clear set-up sequence. The scoped setup avoids clumpy redundant lines where the same variable is accessed over and over.

let questionLabel = UILabel()
questionLabel.textAlignment = .Center
questionLabel.font = UIFont(name:"DnealianManuscript", size: 72)
questionLabel.text = questionText
questionLabel.numberOfLines = 0
In the case of non-reference types, a constant's fields may be set-up sequentially without forcing the constant to be a variable.

<with.md · GitHub Design

This proposal introduces a with function that enables modification and use of an instance using positional references. It's not quite as clean as a solution with implicit self but it offers sufficient utility that a vast swath of Swift developers have adopted this function in some form or another.

@discardableResult
public func with<T>(_ item: T, update: @noescape (inout T) throws -> Void) rethrows -> T {
    var this = item; try update(&this); return this
}
In use:

struct Person { var name: String, favoriteColor: UIColor }
let john = Person(name: "John", favoriteColor: .blueColor())
let jane = with(john){ $0.name = "Jane" }
print(jane) // Person(name: "Jane", favoriteColor: UIDeviceRGBColorSpace 0 0 1 1)

struct Point { var (x, y) : (Double, Double) }
let p1 = Point(x: 2, y: 3)
let p2 = with(p1){ $0.y = 4 }
print(p1, p2) // Point(x: 2.0, y: 3.0) Point(x: 2.0, y: 4.0)
<with.md · GitHub on Existing Code

This proposal is purely additive and has no impact on existing code

<with.md · GitHub Considered

Not adopting this proposal

3 Likes

I like this pretty well, and I think "with()" makes sense as a peer of
"withUnsafePointer()", "withExtendedLifetime()", etc.

I'd also be okay with waiting for a comprehensive method-cascading
solution. I don't find this issue particularly urgent, because it's pretty
easily solvable with an extension or just using closures.

···

On Wed, May 25, 2016 at 11:28 AM, Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

Over the past couple of days, the Twitters have discovered some work I'd
done on closure-based setup.
It's clear that a demand is out there and strong for this kind of
behavior, even without implicit `self` as
part of the mix or cascading. In that light, I've put together the
following:

with.md · GitHub

If the community demand is this high, I think we should re-consider
pushing it before 3.
Feedback as always welcome, including criticism.

-- E

Introducing with to the Standard Library

   - Proposal: TBD
   - Author: Erica Sadun <https://github.com/erica&gt;
   - Status: TBD
   - Review manager: TBD

<with.md · GitHub;
Introduction

This proposal introduces a with function to the standard library to
simplify the initialization and modification of constants and
Foundation-sourced complex objects.

Swift-evolution thread: What about a VBA style with Statement?
<http://thread.gmane.org/gmane.comp.lang.swift.evolution/14384&gt;
<with.md · GitHub;
Motivation

Closure-based initialization enables clean and highly directed set-up in
Swift code. Numerous variations on the theme have been introduced on the
Swift Evolution list and in third party github repositories. Although you
can build solutions natively without functions, current Swift technology
has drawbacks:

let questionLabel: UILabel = {
    $0.textAlignment = .Center
    $0.font = UIFont(name:"DnealianManuscript", size: 72)
    $0.text = questionText
    $0.numberOfLines = 0
    return $0
}(UILabel())
let mySwitch : UISwitch = {
    view.addSubview($0)
    CenterViewInSuperview($0, horizontal: true, vertical: true)
    $0.addTarget(self, action: "action", forControlEvents: .TouchUpInside)
    return $0
}(UISwitch())

   - Assignment must be explicitly typed.
   - The source item must be postpended to the set-up closure.
   - The closure must return the item.
   - This approach is better suited to setting up Foundation objects than
   modifying Swift constants. When duplicating and modifying a constant, the
   closure must create a var copy and modify that copy.

While the implementation is imperfect, the wins are notable. Code
naturally groups into a clear set-up sequence. The scoped setup avoids
clumpy redundant lines where the same variable is accessed over and over.

let questionLabel = UILabel()
questionLabel.textAlignment = .Center
questionLabel.font = UIFont(name:"DnealianManuscript", size: 72)
questionLabel.text = questionText
questionLabel.numberOfLines = 0

In the case of non-reference types, a constant's fields may be set-up
sequentially without forcing the constant to be a variable.

<with.md · GitHub
Design

This proposal introduces a with function that enables modification and
use of an instance using positional references. It's not quite as clean as
a solution with implicit self but it offers sufficient utility that a
vast swath of Swift developers have adopted this function in some form or
another.

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

In use:

struct Person { var name: String, favoriteColor: UIColor }let john = Person(name: "John", favoriteColor: .blueColor())let jane = with(john){ $0.name = "Jane" }print(jane) // Person(name: "Jane", favoriteColor: UIDeviceRGBColorSpace 0 0 1 1)
struct Point { var (x, y) : (Double, Double) }let p1 = Point(x: 2, y: 3)let p2 = with(p1){ $0.y = 4 }print(p1, p2) // Point(x: 2.0, y: 3.0) Point(x: 2.0, y: 4.0)

<with.md · GitHub
on Existing Code

This proposal is purely additive and has no impact on existing code

<with.md · GitHub
Considered
Not adopting this proposal

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

Hi all,

I really like this idea and appreciate the effort. However, I’d rather have a method similar to Ruby Object#tap <APIdock; and not a free function.

Well, since there’s no "Object class" in Swift, the way I see it is through a protocol, much like the Then <https://github.com/devxoul/Then&gt; project is done.

— A

···

On May 25, 2016, at 2:28 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Over the past couple of days, the Twitters have discovered some work I'd done on closure-based setup.
It's clear that a demand is out there and strong for this kind of behavior, even without implicit `self` as
part of the mix or cascading. In that light, I've put together the following:

with.md · GitHub

If the community demand is this high, I think we should re-consider pushing it before 3.
Feedback as always welcome, including criticism.

-- E

Introducing with to the Standard Library

Proposal: TBD
Author: Erica Sadun <https://github.com/erica&gt;
Status: TBD
Review manager: TBD
<with.md · GitHub

This proposal introduces a with function to the standard library to simplify the initialization and modification of constants and Foundation-sourced complex objects.

Swift-evolution thread: What about a VBA style with Statement? <http://thread.gmane.org/gmane.comp.lang.swift.evolution/14384&gt;
<with.md · GitHub

Closure-based initialization enables clean and highly directed set-up in Swift code. Numerous variations on the theme have been introduced on the Swift Evolution list and in third party github repositories. Although you can build solutions natively without functions, current Swift technology has drawbacks:

let questionLabel: UILabel = {
    $0.textAlignment = .Center
    $0.font = UIFont(name:"DnealianManuscript", size: 72)
    $0.text = questionText
    $0.numberOfLines = 0
    return $0
}(UILabel())

let mySwitch : UISwitch = {
    view.addSubview($0)
    CenterViewInSuperview($0, horizontal: true, vertical: true)
    $0.addTarget(self, action: "action", forControlEvents: .TouchUpInside)
    return $0
}(UISwitch())
Assignment must be explicitly typed.
The source item must be postpended to the set-up closure.
The closure must return the item.
This approach is better suited to setting up Foundation objects than modifying Swift constants. When duplicating and modifying a constant, the closure must create a var copy and modify that copy.
While the implementation is imperfect, the wins are notable. Code naturally groups into a clear set-up sequence. The scoped setup avoids clumpy redundant lines where the same variable is accessed over and over.

let questionLabel = UILabel()
questionLabel.textAlignment = .Center
questionLabel.font = UIFont(name:"DnealianManuscript", size: 72)
questionLabel.text = questionText
questionLabel.numberOfLines = 0
In the case of non-reference types, a constant's fields may be set-up sequentially without forcing the constant to be a variable.

<with.md · GitHub Design

This proposal introduces a with function that enables modification and use of an instance using positional references. It's not quite as clean as a solution with implicit self but it offers sufficient utility that a vast swath of Swift developers have adopted this function in some form or another.

@discardableResult
public func with<T>(_ item: T, update: @noescape (inout T) throws -> Void) rethrows -> T {
    var this = item; try update(&this); return this
}
In use:

struct Person { var name: String, favoriteColor: UIColor }
let john = Person(name: "John", favoriteColor: .blueColor())
let jane = with(john){ $0.name = "Jane" }
print(jane) // Person(name: "Jane", favoriteColor: UIDeviceRGBColorSpace 0 0 1 1)

struct Point { var (x, y) : (Double, Double) }
let p1 = Point(x: 2, y: 3)
let p2 = with(p1){ $0.y = 4 }
print(p1, p2) // Point(x: 2.0, y: 3.0) Point(x: 2.0, y: 4.0)
<with.md · GitHub on Existing Code

This proposal is purely additive and has no impact on existing code

<with.md · GitHub Considered

Not adopting this proposal
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Recently there was another possible 'with' variant:
- instead of being a: with 'instance' create new 'instance' using 'closure'
- it was a: with(/on) 'instance/s' perform 'closure' (same idea as the closure-based initialization, described in the motivation)

The thread "What about a VBA style with Statement?": http://thread.gmane.org/gmane.comp.lang.swift.evolution/14384

In either case the 'with' can be misleading/confusing; with, on its own doesn't suggest creation of a new entity, nor does it strongly suggest possible mutation.

Just mentioning it as to end up... with the proper name for this new function.

Dany

···

Le 25 mai 2016 à 14:28, Erica Sadun via swift-evolution <swift-evolution@swift.org> a écrit :

Over the past couple of days, the Twitters have discovered some work I'd done on closure-based setup.
It's clear that a demand is out there and strong for this kind of behavior, even without implicit `self` as
part of the mix or cascading. In that light, I've put together the following:

with.md · GitHub

If the community demand is this high, I think we should re-consider pushing it before 3.
Feedback as always welcome, including criticism.

Hi all,

I really like this idea and appreciate the effort. However, I’d rather have a method similar to Ruby Object#tap <APIdock; and not a free function.

Well, since there’s no "Object class" in Swift, the way I see it is through a protocol, much like the Then <https://github.com/devxoul/Then&gt; project is done.

— A

In Swift, there's no root object like NSObject, which is what the Then project relies upon. I'm unfamiliar with `tap` but it looks
similar to method cascading, which is on hold for Swift 4.

-- E

···

On May 25, 2016, at 1:00 PM, Adriano Ferreira <adriano.ferreira@me.com> wrote:

On May 25, 2016, at 2:28 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Over the past couple of days, the Twitters have discovered some work I'd done on closure-based setup.
It's clear that a demand is out there and strong for this kind of behavior, even without implicit `self` as
part of the mix or cascading. In that light, I've put together the following:

with.md · GitHub

If the community demand is this high, I think we should re-consider pushing it before 3.
Feedback as always welcome, including criticism.

I'm unfamiliar with `tap` but it looks
similar to method cascading, which is on hold for Swift 4.

I *am* familiar with `tap`. It's basically `with(_:update:)`, but as a method and not `inout`, which usually doesn't matter for Ruby since everything is an object in that language. Typically, though, `tap` is not used to mutate at all; instead, it's used to access an intermediate value in a method chain, for instance to log it.

I am very much in favor of `with(_:update:)`. Fundamentally, it is an operation which applies a mutating method to a copy to make it nonmutating; this seems like a very useful thing to have in the standard library. I could easily imagine the standard library itself using it to reduce boilerplate. For instance, if the FloatingPoint protocol currently offers this:

  extension FloatingPoint {
    func adding(_ other: Self) -> Self {
      var selfCopy = self
      selfCopy.add(other)
      return selfCopy
    }
  }

`with(_:update:)` would allow us to instead shorten it to:

  extension FloatingPoint {
    func adding(_ other: Self) -> Self {
      return with(self) { $0.add(other) }
    }
  }

I also think this is fairly future-proof. Many of the future proposals in this area, like `self` shadowing, will interoperate with `with(_:update:)` perfectly. Others, like method cascading, would supplant it in some uses, but still leave many others (like the `FloatingPoint` example above) untouched. But even without any other changes at all, this is a good, useful thing to have in the language.

I don't agree with those who argue that this should be a method. When Swift offers an operation on any instance, it very rarely does so as a member (and `dynamicType`, one of the few exceptions, seems to be going away); instead, it uses either a free function or an initializer on the result type, depending on the circumstance. A `with(_:update:)` free function thus matches a broader pattern in the language.

There are also implementation reasons not to go the method route. A free function can be trivially implemented in the standard library with no language changes at all. A method, on the other hand, would either have to be implemented as a keyword specially recognized by the parser and handled by the language (as `dynamicType` is), or would require us to allow the currently forbidden extension of `Any`. Either of these is vastly more difficult and risky than using a free function.

···

--
Brent Royal-Gordon
Architechies

I like this pretty well, and I think "with()" makes sense as a peer of "withUnsafePointer()", "withExtendedLifetime()", etc.

I'd also be okay with waiting for a comprehensive method-cascading solution. I don't find this issue particularly urgent, because it's pretty easily solvable with an extension or just using closures.

+1. I’ve been playing around with it in my own code a little bit. I’m still uncertain about when I think it is good style and when I think it is best avoided. I would certainly feel more comfortable using it if it was in the standard library.

At the same time, I think we can and should do better in the future. If that is the plan, do we really want `with` in the standard library? I don’t mind waiting for a better solution, especially if it means a better solution is more likely to happen and / or we aren’t left with an unnecessary and duplicative function in the standard library.

So I’m on the fence here.

···

On May 25, 2016, at 3:48 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

On Wed, May 25, 2016 at 11:28 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Over the past couple of days, the Twitters have discovered some work I'd done on closure-based setup.
It's clear that a demand is out there and strong for this kind of behavior, even without implicit `self` as
part of the mix or cascading. In that light, I've put together the following:

with.md · GitHub

If the community demand is this high, I think we should re-consider pushing it before 3.
Feedback as always welcome, including criticism.

-- E

Introducing with to the Standard Library

Proposal: TBD
Author: Erica Sadun <https://github.com/erica&gt;
Status: TBD
Review manager: TBD
<with.md · GitHub

This proposal introduces a with function to the standard library to simplify the initialization and modification of constants and Foundation-sourced complex objects.

Swift-evolution thread: What about a VBA style with Statement? <http://thread.gmane.org/gmane.comp.lang.swift.evolution/14384&gt;
<with.md · GitHub

Closure-based initialization enables clean and highly directed set-up in Swift code. Numerous variations on the theme have been introduced on the Swift Evolution list and in third party github repositories. Although you can build solutions natively without functions, current Swift technology has drawbacks:

let questionLabel: UILabel = {
    $0.textAlignment = .Center
    $0.font = UIFont(name:"DnealianManuscript", size: 72)
    $0.text = questionText
    $0.numberOfLines = 0
    return $0
}(UILabel())

let mySwitch : UISwitch = {
    view.addSubview($0)
    CenterViewInSuperview($0, horizontal: true, vertical: true)
    $0.addTarget(self, action: "action", forControlEvents: .TouchUpInside)
    return $0
}(UISwitch())
Assignment must be explicitly typed.
The source item must be postpended to the set-up closure.
The closure must return the item.
This approach is better suited to setting up Foundation objects than modifying Swift constants. When duplicating and modifying a constant, the closure must create a var copy and modify that copy.
While the implementation is imperfect, the wins are notable. Code naturally groups into a clear set-up sequence. The scoped setup avoids clumpy redundant lines where the same variable is accessed over and over.

let questionLabel = UILabel()
questionLabel.textAlignment = .Center
questionLabel.font = UIFont(name:"DnealianManuscript", size: 72)
questionLabel.text = questionText
questionLabel.numberOfLines = 0
In the case of non-reference types, a constant's fields may be set-up sequentially without forcing the constant to be a variable.

<with.md · GitHub Design

This proposal introduces a with function that enables modification and use of an instance using positional references. It's not quite as clean as a solution with implicit self but it offers sufficient utility that a vast swath of Swift developers have adopted this function in some form or another.

@discardableResult
public func with<T>(_ item: T, update: @noescape (inout T) throws -> Void) rethrows -> T {
    var this = item; try update(&this); return this
}
In use:

struct Person { var name: String, favoriteColor: UIColor }
let john = Person(name: "John", favoriteColor: .blueColor())
let jane = with(john){ $0.name = "Jane" }
print(jane) // Person(name: "Jane", favoriteColor: UIDeviceRGBColorSpace 0 0 1 1)

struct Point { var (x, y) : (Double, Double) }
let p1 = Point(x: 2, y: 3)
let p2 = with(p1){ $0.y = 4 }
print(p1, p2) // Point(x: 2.0, y: 3.0) Point(x: 2.0, y: 4.0)
<with.md · GitHub on Existing Code

This proposal is purely additive and has no impact on existing code

<with.md · GitHub Considered

Not adopting this proposal

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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

Naming can always be bikeshedded.
Brent's submitted a pull request: Introducing with to the Standard Library by beccadax · Pull Request #346 · apple/swift-evolution · GitHub
Here's a gist: https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb
The current state of the proposal follows
-- E

Introducing with to the Standard Library

Proposal: TBD
Author: Erica Sadun <https://github.com/erica&gt;, Brent Royal-Gordon <https://github.com/brentdax&gt;
Status: TBD
Review manager: TBD
<https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb#introduction&gt;Introduction

This proposal introduces a with function to the standard library. This function simplifies the initialization of objects and modification of value types.

Swift-evolution thread: What about a VBA style with Statement? <http://thread.gmane.org/gmane.comp.lang.swift.evolution/14384&gt;
<https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb#motivation&gt;Motivation

When setting up or modifying an instance, developers sometimes use an immediately-called closure to introduce a short alias for the instance and group the modification code together. For example, they may initialize and customize a Cocoa object:

let questionLabel: UILabel = {
    $0.textAlignment = .Center
    $0.font = UIFont(name: "DnealianManuscript", size: 72)
    $0.text = questionText
    $0.numberOfLines = 0
    mainView.addSubview($0)
    return $0
}(UILabel())
Or they may duplicate and modify a constant value-typed instance:

let john = Person(name: "John", favoriteColor: .blueColor())
let jane: Person = { (var copy) in
    copy.name = "Jane"
    return copy
}(john)
This technique has many drawbacks:

The compiler cannot infer the return type.
You must explicitly return the modified instance.
The instance being used comes after, not before, the code using it.
Nevertheless, developers have created many variations on this theme, because they are drawn to its benefits:

The short, temporary name reduces noise compared to repeating a variable name like questionLabel.
The block groups together the initialization code.
The scope of mutability is limited.
SE-0003, which removes var parameters <https://github.com/apple/swift-evolution/blob/master/proposals/0003-remove-var-parameters.md&gt;, will make this situation even worse by requiring a second line of boilerplate for value types. And yet developers will probably keep using these sorts of tricks.

Fundamentally, this is a very simple and common pattern: creating a temporary mutable variable confined to a short scope, whose value will later be used immutably in a wider scope. Moreover, this pattern shortens the scopes of mutable variables, so it is something we should encourage. We believe it's worth codifying in the standard library.

<https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb#proposed-solution&gt;Proposed Solution

We propose introducing a function with the following simplified signature:

func with<T>(_: T, update: (inout T -> Void)) -> T
with assigns the value to a new variable, passes that variable as a parameter to the closure, and then returns the potentially modified variable. That means:

When used with value types, the closure can modify a copy of the original value.
When used with reference types, the closure can substitute a different instance for the original, perhaps by calling copy() or some non-Cocoa equivalent.
The closure does not actually have to modify the parameter; it can merely use it, or (for a reference type) modify the object without changing the reference.

<https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb#examples&gt;Examples

<https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb#initializing-a-cocoa-object&gt;Initializing a Cocoa Object

Before:

let questionLabel: UILabel = {
    $0.textAlignment = .Center
    $0.font = UIFont(name: "DnealianManuscript", size: 72)
    $0.text = questionText
    $0.numberOfLines = 0
    mainView.addSubview($0)
    return $0
}(UILabel())
After:

let questionLabel = with(UILabel()) {
    $0.textAlignment = .Center
    $0.font = UIFont(name: "DnealianManuscript", size: 72)
    $0.text = questionText
    $0.numberOfLines = 0
    mainView.addSubview($0)
}
Using with here moves the UILabel() initialization to the top, allows the type of questionLabel to be inferred, and removes the return statement.

<https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb#copying-and-modifying-a-constant&gt;Copying and Modifying a Constant

Before (without var parameter):

let john = Person(name: "John", favoriteColor: .blueColor())
let jane: Person = {
    var copy = $0
    copy.name = "Jane"
    return copy
}(john)
After:

let john = Person(name: "John", favoriteColor: .blueColor())
let jane = with(john) {
    $0.name = "Jane"
}
In addition to the aforementioned benefits, with removes the var copy line.

<https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb#treating-a-mutable-method-as-a-copy-and-return-method&gt;Treating a Mutable Method As a Copy-and-Return Method

You would like to write this:

let fewerFoos = foos.removing(at: i)
But there is only a remove(at:) mutating method. Using with, you can write:

let fewerFoos = with(foos) { $0.remove(at: i) }
<https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb#avoiding-mutable-shadowing&gt;Avoiding Mutable Shadowing

The standard library includes an operator <https://github.com/apple/swift/blob/690d98a078a214557cd8f731b6215336bbc18a77/stdlib/public/core/RangeReplaceableCollection.swift.gyb#L1169&gt; for concatenating two RangeReplaceableCollections with this implementation:

var lhs = lhs
// FIXME: what if lhs is a reference type? This will mutate it.
lhs.reserveCapacity(lhs.count + numericCast(rhs.count))
lhs.append(contentsOf: rhs)
return lhs
Using with, you can eliminate the shadowing of lhs:

// FIXME: what if lhs is a reference type? This will mutate it.
return with(lhs) {
  $0.reserveCapacity($0.count + numericCast(rhs.count))
  $0.append(contentsOf: rhs)
}
It's important to note that with does not resolve the "FIXME" comment. Like the var lhs = lhs in the original code, with only copies value types, not reference types. If RangeReplaceableCollection included a Foundation-like copy()method that was guaranteed to return a copy even if it was a reference type, with would work nicely with that solution:

return with(lhs.copy()) {
  $0.reserveCapacity($0.count + numericCast(rhs.count))
  $0.append(contentsOf: rhs)
}
<https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb#inspecting-an-intermediate-value&gt;Inspecting an Intermediate Value

Suppose you want to inspect a value in the middle of a long method chain. For instance, you're not sure this is retrieving the type of cell you expect:

let view = tableView.cellForRow(at: indexPath)?.contentView.withTag(42)
Currently, you would need to either split the statement in two so you could capture the return value of cellForRow(at:) in a constant, or insert a very clunky immediate-closure call in the middle of the statement. Using with, you can stay close to the original expression:

let view = with(tableView.cellForRow(at: indexPath)) { print($0) }?.contentView.withTag(42)
Because the closure doesn't alter $0, the cell passes through the with call unaltered, so it can be used by the rest of the method chain.

<https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb#detailed-design&gt;Detailed Design

We propose adding the following free function to the standard library:

/// 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.
@discardableResult
public func with<T>(_ item: T, update: @noescape (inout T) throws -> Void) rethrows -> T {
  var this = item
  try update(&this)
  return this
}
@discardableResult permits the use of with(_:update:) to create a scoped temporary copy of the value with a shorter name.

<https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb#impact-on-existing-code&gt;Impact on Existing Code

This proposal is purely additive and has no impact on existing code.

<https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb#alternatives-considered&gt;Alternatives Considered

Doing nothing: with is a mere convenience; any code using it could be written another way. If rejected, users could continue to write code using the longhand form, the various closure-based techniques, or homegrown versions of with.

Using method syntax: Some list members preferred a syntax that looked more like a method call with a trailing closure:

let questionLabel = UILabel().with {
    $0.textAlignment = .Center
    $0.font = UIFont(name: "DnealianManuscript", size: 72)
    $0.numberOfLines = 0
    addSubview($0)
}
This would require a more drastic solution as it's not possible to add methods to all Swift types. Nor does it match the existing design of functions like withExtendedLifetime(_:_:), withUnsafePointer(_:_:), and reflect(_:).

Adding self rebinding: Some list members wanted a way to bind self to the passed argument, so that they can use implicit self to eliminate $0.:

let supView = self
let questionLabel = with(UILabel()) {
    self in
    textAlignment = .Center
    font = UIFont(name: "DnealianManuscript", size: 72)
    numberOfLines = 0
    supView.addSubview(self)
}
We do not believe this is practical to propose in the Swift 3 timeframe, and we believe with would work well with this feature if it were added later.

Adding method cascades: A competing proposal was to introduce a way to use several methods or properties on the same instance; Dart and Smalltalk have features of this kind.

let questionLabel = UILabel()
    ..textAlignment = .Center
    ..font = UIFont(name: "DnealianManuscript", size: 72)
    ..numberOfLines = 0
addSubview(questionLabel)
Like rebinding self, we do not believe method cascades are practical for the Swift 3 timeframe. We also believe that many of with's use cases would not be subsumed by method cascades even if they were added.

···

On May 25, 2016, at 5:34 PM, Dany St-Amant <dsa.mls@icloud.com> wrote:

Le 25 mai 2016 à 14:28, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

Over the past couple of days, the Twitters have discovered some work I'd done on closure-based setup.
It's clear that a demand is out there and strong for this kind of behavior, even without implicit `self` as
part of the mix or cascading. In that light, I've put together the following:

with.md · GitHub

If the community demand is this high, I think we should re-consider pushing it before 3.
Feedback as always welcome, including criticism.

Recently there was another possible 'with' variant:
- instead of being a: with 'instance' create new 'instance' using 'closure'
- it was a: with(/on) 'instance/s' perform 'closure' (same idea as the closure-based initialization, described in the motivation)

The thread "What about a VBA style with Statement?": http://thread.gmane.org/gmane.comp.lang.swift.evolution/14384

In either case the 'with' can be misleading/confusing; with, on its own doesn't suggest creation of a new entity, nor does it strongly suggest possible mutation.

Just mentioning it as to end up... with the proper name for this new function.

Dany

I wouldn't be pushing if I thought it wouldn't be useful after cascading. If no other reason, it offers a way to duplicate/modify value types to be stored into constants. That alone should argue for its value.

-- E

···

On May 25, 2016, at 2:55 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 25, 2016, at 3:48 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I like this pretty well, and I think "with()" makes sense as a peer of "withUnsafePointer()", "withExtendedLifetime()", etc.

I'd also be okay with waiting for a comprehensive method-cascading solution. I don't find this issue particularly urgent, because it's pretty easily solvable with an extension or just using closures.

+1. I’ve been playing around with it in my own code a little bit. I’m still uncertain about when I think it is good style and when I think it is best avoided. I would certainly feel more comfortable using it if it was in the standard library.

At the same time, I think we can and should do better in the future. If that is the plan, do we really want `with` in the standard library? I don’t mind waiting for a better solution, especially if it means a better solution is more likely to happen and / or we aren’t left with an unnecessary and duplicative function in the standard library.

So I’m on the fence here.

I like this pretty well, and I think "with()" makes sense as a peer of "withUnsafePointer()", "withExtendedLifetime()", etc.

I'd also be okay with waiting for a comprehensive method-cascading solution. I don't find this issue particularly urgent, because it's pretty easily solvable with an extension or just using closures.

+1. I’ve been playing around with it in my own code a little bit. I’m still uncertain about when I think it is good style and when I think it is best avoided. I would certainly feel more comfortable using it if it was in the standard library.

At the same time, I think we can and should do better in the future. If that is the plan, do we really want `with` in the standard library? I don’t mind waiting for a better solution, especially if it means a better solution is more likely to happen and / or we aren’t left with an unnecessary and duplicative function in the standard library.

So I’m on the fence here.

I wouldn't be pushing if I thought it wouldn't be useful after cascading. If no other reason, it offers a way to duplicate/modify value types to be stored into constants. That alone should argue for its value.

Can you point us to the latest draft of what you have in mind for cascading in the future?

···

Sent from my iPad

On May 25, 2016, at 3:56 PM, Erica Sadun <erica@ericasadun.com> wrote:

On May 25, 2016, at 2:55 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 25, 2016, at 3:48 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

-- E

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:

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

- 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.

  /// 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

Hi,
I really like the proposal as it's in the last draft. And the examples are really good, specially the one that is similar to the Ruby tap.
I've been using the Then extension for a while and makes the initialization much nicer. I also use a set of extensions that allow me to print intermediate objects in a chain of methods. Initially I was using it for FRP style but I ended up using it while developing on chain of functional methods on arrays. Is basically a specific implementation of what this proposal will generalize, which is cool.

My only concern is with the method cascading proposal. If that gets implement in the future how useful will this proposal be? I'm not that familiar with method cascading to have a strong opinion on this, but I would like to see other members to speak about it.

Btw, thanks for adding the proposals directly in the email. Makes keeping up to date withthe list much easier :clap:t2:

···

Sent from my iPad

On 27 May 2016, at 20:42, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

On May 25, 2016, at 5:34 PM, Dany St-Amant <dsa.mls@icloud.com> wrote:

Le 25 mai 2016 à 14:28, Erica Sadun via swift-evolution <swift-evolution@swift.org> a écrit :

Over the past couple of days, the Twitters have discovered some work I'd done on closure-based setup.
It's clear that a demand is out there and strong for this kind of behavior, even without implicit `self` as
part of the mix or cascading. In that light, I've put together the following:

with.md · GitHub

If the community demand is this high, I think we should re-consider pushing it before 3.
Feedback as always welcome, including criticism.

Recently there was another possible 'with' variant:
- instead of being a: with 'instance' create new 'instance' using 'closure'
- it was a: with(/on) 'instance/s' perform 'closure' (same idea as the closure-based initialization, described in the motivation)

The thread "What about a VBA style with Statement?": http://thread.gmane.org/gmane.comp.lang.swift.evolution/14384

In either case the 'with' can be misleading/confusing; with, on its own doesn't suggest creation of a new entity, nor does it strongly suggest possible mutation.

Just mentioning it as to end up... with the proper name for this new function.

Dany

Naming can always be bikeshedded.
Brent's submitted a pull request: Introducing with to the Standard Library by beccadax · Pull Request #346 · apple/swift-evolution · GitHub
Here's a gist: https://gist.github.com/brentdax/ce3272e3d35f5ccac56483666f86b8fb
The current state of the proposal follows
-- E

Introducing with to the Standard Library
Proposal: TBD
Author: Erica Sadun, Brent Royal-Gordon
Status: TBD
Review manager: TBD
Introduction

This proposal introduces a with function to the standard library. This function simplifies the initialization of objects and modification of value types.

Swift-evolution thread: What about a VBA style with Statement?

Motivation

When setting up or modifying an instance, developers sometimes use an immediately-called closure to introduce a short alias for the instance and group the modification code together. For example, they may initialize and customize a Cocoa object:

let questionLabel: UILabel = {
    $0.textAlignment = .Center
    $0.font = UIFont(name: "DnealianManuscript", size: 72)
    $0.text = questionText
    $0.numberOfLines = 0
    mainView.addSubview($0)
    return $0
}(UILabel())
Or they may duplicate and modify a constant value-typed instance:

let john = Person(name: "John", favoriteColor: .blueColor())
let jane: Person = { (var copy) in
    copy.name = "Jane"
    return copy
}(john)
This technique has many drawbacks:

The compiler cannot infer the return type.
You must explicitly return the modified instance.
The instance being used comes after, not before, the code using it.
Nevertheless, developers have created many variations on this theme, because they are drawn to its benefits:

The short, temporary name reduces noise compared to repeating a variable name like questionLabel.
The block groups together the initialization code.
The scope of mutability is limited.
SE-0003, which removes var parameters, will make this situation even worse by requiring a second line of boilerplate for value types. And yet developers will probably keep using these sorts of tricks.

Fundamentally, this is a very simple and common pattern: creating a temporary mutable variable confined to a short scope, whose value will later be used immutably in a wider scope. Moreover, this pattern shortens the scopes of mutable variables, so it is something we should encourage. We believe it's worth codifying in the standard library.

Proposed Solution

We propose introducing a function with the following simplified signature:

func with<T>(_: T, update: (inout T -> Void)) -> T
with assigns the value to a new variable, passes that variable as a parameter to the closure, and then returns the potentially modified variable. That means:

When used with value types, the closure can modify a copy of the original value.
When used with reference types, the closure can substitute a different instance for the original, perhaps by calling copy() or some non-Cocoa equivalent.
The closure does not actually have to modify the parameter; it can merely use it, or (for a reference type) modify the object without changing the reference.

Examples

Initializing a Cocoa Object

Before:

let questionLabel: UILabel = {
    $0.textAlignment = .Center
    $0.font = UIFont(name: "DnealianManuscript", size: 72)
    $0.text = questionText
    $0.numberOfLines = 0
    mainView.addSubview($0)
    return $0
}(UILabel())
After:

let questionLabel = with(UILabel()) {
    $0.textAlignment = .Center
    $0.font = UIFont(name: "DnealianManuscript", size: 72)
    $0.text = questionText
    $0.numberOfLines = 0
    mainView.addSubview($0)
}
Using with here moves the UILabel() initialization to the top, allows the type of questionLabel to be inferred, and removes the return statement.

Copying and Modifying a Constant

Before (without var parameter):

let john = Person(name: "John", favoriteColor: .blueColor())
let jane: Person = {
    var copy = $0
    copy.name = "Jane"
    return copy
}(john)
After:

let john = Person(name: "John", favoriteColor: .blueColor())
let jane = with(john) {
    $0.name = "Jane"
}
In addition to the aforementioned benefits, with removes the var copy line.

Treating a Mutable Method As a Copy-and-Return Method

You would like to write this:

let fewerFoos = foos.removing(at: i)
But there is only a remove(at:) mutating method. Using with, you can write:

let fewerFoos = with(foos) { $0.remove(at: i) }
Avoiding Mutable Shadowing

The standard library includes an operator for concatenating two RangeReplaceableCollections with this implementation:

var lhs = lhs
// FIXME: what if lhs is a reference type? This will mutate it.
lhs.reserveCapacity(lhs.count + numericCast(rhs.count))
lhs.append(contentsOf: rhs)
return lhs
Using with, you can eliminate the shadowing of lhs:

// FIXME: what if lhs is a reference type? This will mutate it.
return with(lhs) {
  $0.reserveCapacity($0.count + numericCast(rhs.count))
  $0.append(contentsOf: rhs)
}
It's important to note that with does not resolve the "FIXME" comment. Like the var lhs = lhs in the original code, with only copies value types, not reference types. If RangeReplaceableCollection included a Foundation-like copy()method that was guaranteed to return a copy even if it was a reference type, with would work nicely with that solution:

return with(lhs.copy()) {
  $0.reserveCapacity($0.count + numericCast(rhs.count))
  $0.append(contentsOf: rhs)
}
Inspecting an Intermediate Value

Suppose you want to inspect a value in the middle of a long method chain. For instance, you're not sure this is retrieving the type of cell you expect:

let view = tableView.cellForRow(at: indexPath)?.contentView.withTag(42)
Currently, you would need to either split the statement in two so you could capture the return value of cellForRow(at:) in a constant, or insert a very clunky immediate-closure call in the middle of the statement. Using with, you can stay close to the original expression:

let view = with(tableView.cellForRow(at: indexPath)) { print($0) }?.contentView.withTag(42)
Because the closure doesn't alter $0, the cell passes through the with call unaltered, so it can be used by the rest of the method chain.

Detailed Design

We propose adding the following free function to the standard library:

/// 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.
@discardableResult
public func with<T>(_ item: T, update: @noescape (inout T) throws -> Void) rethrows -> T {
  var this = item
  try update(&this)
  return this
}
@discardableResult permits the use of with(_:update:) to create a scoped temporary copy of the value with a shorter name.

Impact on Existing Code

This proposal is purely additive and has no impact on existing code.

Alternatives Considered

Doing nothing: with is a mere convenience; any code using it could be written another way. If rejected, users could continue to write code using the longhand form, the various closure-based techniques, or homegrown versions of with.

Using method syntax: Some list members preferred a syntax that looked more like a method call with a trailing closure:

let questionLabel = UILabel().with {
    $0.textAlignment = .Center
    $0.font = UIFont(name: "DnealianManuscript", size: 72)
    $0.numberOfLines = 0
    addSubview($0)
}
This would require a more drastic solution as it's not possible to add methods to all Swift types. Nor does it match the existing design of functions like withExtendedLifetime(_:_:), withUnsafePointer(_:_:), and reflect(_:).

Adding self rebinding: Some list members wanted a way to bind self to the passed argument, so that they can use implicit self to eliminate $0.:

let supView = self
let questionLabel = with(UILabel()) {
    self in
    textAlignment = .Center
    font = UIFont(name: "DnealianManuscript", size: 72)
    numberOfLines = 0
    supView.addSubview(self)
}
We do not believe this is practical to propose in the Swift 3 timeframe, and we believe with would work well with this feature if it were added later.

Adding method cascades: A competing proposal was to introduce a way to use several methods or properties on the same instance; Dart and Smalltalk have features of this kind.

let questionLabel = UILabel()
    ..textAlignment = .Center
    ..font = UIFont(name: "DnealianManuscript", size: 72)
    ..numberOfLines = 0
addSubview(questionLabel)
Like rebinding self, we do not believe method cascades are practical for the Swift 3 timeframe. We also believe that many of with's use cases would not be subsumed by method cascades even if they were added.

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

Haven't really touched it since I got the "sit and wait" but I've come around to preferring Dartism.

-- E

···

On May 25, 2016, at 3:29 PM, Matthew Johnson <matthew@anandabits.com> wrote:
On May 25, 2016, at 3:56 PM, Erica Sadun <erica@ericasadun.com <mailto:erica@ericasadun.com>> wrote:

I wouldn't be pushing if I thought it wouldn't be useful after cascading. If no other reason, it offers a way to duplicate/modify value types to be stored into constants. That alone should argue for its value.

Can you point us to the latest draft of what you have in mind for cascading in the future?

I wouldn't be pushing if I thought it wouldn't be useful after cascading. If no other reason, it offers a way to duplicate/modify value types to be stored into constants. That alone should argue for its value.

Can you point us to the latest draft of what you have in mind for cascading in the future?

Haven't really touched it since I got the "sit and wait" but I've come around to preferring Dartism.

Got it. I just looked up how Dart does this to refresh my memory.

Are there cases where you think `with` would be a better option than method cascading other than the copy scenario?

I think there are several ways we could handle the “copy and modify a value type” use case better than `with`. The most obvious: if the primary reason for `with` to exist post-method cascading is to introduce a copy, I would prefer a `copy` function that could be used in conjunction with method cascading (which would then mutate and return the copy rather than the initial instance).

Brent’s example would go from:

  extension FloatingPoint {
    func adding(_ other: Self) -> Self {
      return with(self) { $0.add(other) }
    }
  }

to:

  extension FloatingPoint {
    func adding(_ other: Self) -> Self {
      return copy(self)..add(other)
    }
  }

This reads much better than the former IMO.

I’m interested in hearing about use cases where `with` is superior to method cascading plus `copy`. I think that is the minimum bar that should be crossed if we’re going to add this to the standard library.

I’m not opposed to it on principle, but I want to make sure it carries its weight long term if we put it in the standard library. It’s easy enough to copy into our projects if it’s not there and we feel the need to use it.

···

On May 25, 2016, at 4:31 PM, Erica Sadun <erica@ericasadun.com> wrote:

On May 25, 2016, at 3:29 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:
On May 25, 2016, at 3:56 PM, Erica Sadun <erica@ericasadun.com <mailto:erica@ericasadun.com>> wrote:

-- E

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()

···

On May 27, 2016, at 5:12 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

  /// 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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

- 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.

I would not. It gives you a way to give a value type a short, scoped, immutable alias:

  with(RareMagicalDeviceOwner.shared.spimsterWickets[randomIndex]) {
    print($0.turns)
    print($0.turnSpeed)
  }

And in this form, there is no danger of mistakenly mutating the value type, because mutating methods would not be allowed:

  with(RareMagicalDeviceOwner.shared.spimsterWickets[randomIndex]) {
    $0.turnRepeatedly(times: 3) // Error: can't call mutating method on immutable parameter
  }

To be clear, I'm not convinced there's a need to make any change from the proposed version at all. I'm spitballing alternate designs here, trying to see if there might be something a little better out there. But so far, I think the proposal balances the feature size against strictness pretty well, whereas these stricter designs I'm discussing increase the surface of the feature more than they improve it. This is a small (but significant!) convenience, and I feel pretty strongly that it should have a small implementation.

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?

Yes, absolutely. Method cascades have a narrow use case: methods on `self`. Not everything in Swift is a method, and not all methods are on `self`.

  with(tableView.cellForRow(at: indexPath).myLabel) { label in
    print("Constraining label: \(label)")
    
    NSLayoutConstraint.activate(
      NSLayoutConstraint.withVisualFormat("|-[label]-|", options: , metrics: [:], views: ["label": label]) +
      NSLayoutConstraint.withVisualFormat("V:|[label]|", options: , metrics: [:], views: ["label": label])
    )
    
    constrainedLabels.append(label)
  }

None of the calls in that `with` block would benefit from method cascades, but they all benefit from `with`.

- 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.

I specifically considered and rejected `withCopy` because it only creates a copy of a value type, not a reference type. (Of course, it does create a copy of the reference itself, but that's a very subtle distinction.) I chose `withVar` to make it very clear that you're getting the same semantics as you would for a `var` temporary.

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()

Honestly, I'm not sure there's a coherent way to make method cascades work with your `withCopy` (or the `copy` function you mentioned upthread) at all.

Here's the problem. Suppose you have a property like this:

  var array: [Int]

And then you write this:

  array = [1, 2, 3]
  return array
      ..remove(at: 1)
      ..remove(at: 0)

I assume you think this should not only *return* `[3]`, but also *set* `array` to `[3]`. That's kind of implied by the fact that you think we need a `withCopy(array)` call to protect `array` from being affected by these calls.

But that means that in this version:

  array = [1, 2, 3]
  return withCopy(array)
      ..remove(at: 1)
      ..remove(at: 0)

You are trying to call `mutating` methods on an *immutable* value, the return value of `withCopy`. Normally, the compiler would reject that.

Perhaps you could say that method cascades operate on a copy if the receiver is immutable, but that makes code vague and its behavior subtle and easily changed by accident. For instance, if a property is `internal private(set)`, then moving a method cascade from code which can't see the setter to code which can would silently change the code from immutable to mutable. Similarly, adding the `private(set)` would not cause the code which previously modified it to produce an error; it would instead silently change to no longer mutate where it used to before. That's not acceptable behavior from a language feature.

About the only solution to this I can come up with is to make `withCopy` have an `inout` return. But this at best forms an attractive nuisance: If you use normal `mutating` method calls instead of method cascading, your changes are going to disappear into the ether. And depending on how `inout` returns are actually implemented, it could lead to worse misbehavior.

···

--
Brent Royal-Gordon
Architechies

Just some alternate naming suggestions for with() and withVar(), as the naming guidelines suggest -ed/-ing:

withVar
altered() // Changes a value copy / reference and returns it
mutated() // Or this, but uses value-specific term ‘mutate’

with
inspect() // Works with an immutable copy, has @discardableResult
use()

···

On 28 May 2016, at 10:19 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

- 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.

I would not. It gives you a way to give a value type a short, scoped, immutable alias:

  with(RareMagicalDeviceOwner.shared.spimsterWickets[randomIndex]) {
    print($0.turns)
    print($0.turnSpeed)
  }

And in this form, there is no danger of mistakenly mutating the value type, because mutating methods would not be allowed:

  with(RareMagicalDeviceOwner.shared.spimsterWickets[randomIndex]) {
    $0.turnRepeatedly(times: 3) // Error: can't call mutating method on immutable parameter
  }

To be clear, I'm not convinced there's a need to make any change from the proposed version at all. I'm spitballing alternate designs here, trying to see if there might be something a little better out there. But so far, I think the proposal balances the feature size against strictness pretty well, whereas these stricter designs I'm discussing increase the surface of the feature more than they improve it. This is a small (but significant!) convenience, and I feel pretty strongly that it should have a small implementation.

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?

Yes, absolutely. Method cascades have a narrow use case: methods on `self`. Not everything in Swift is a method, and not all methods are on `self`.

  with(tableView.cellForRow(at: indexPath).myLabel) { label in
    print("Constraining label: \(label)")
    
    NSLayoutConstraint.activate(
      NSLayoutConstraint.withVisualFormat("|-[label]-|", options: , metrics: [:], views: ["label": label]) +
      NSLayoutConstraint.withVisualFormat("V:|[label]|", options: , metrics: [:], views: ["label": label])
    )
    
    constrainedLabels.append(label)
  }

None of the calls in that `with` block would benefit from method cascades, but they all benefit from `with`.

- 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.

I specifically considered and rejected `withCopy` because it only creates a copy of a value type, not a reference type. (Of course, it does create a copy of the reference itself, but that's a very subtle distinction.) I chose `withVar` to make it very clear that you're getting the same semantics as you would for a `var` temporary.

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()

Honestly, I'm not sure there's a coherent way to make method cascades work with your `withCopy` (or the `copy` function you mentioned upthread) at all.

Here's the problem. Suppose you have a property like this:

  var array: [Int]

And then you write this:

  array = [1, 2, 3]
  return array
      ..remove(at: 1)
      ..remove(at: 0)

I assume you think this should not only *return* `[3]`, but also *set* `array` to `[3]`. That's kind of implied by the fact that you think we need a `withCopy(array)` call to protect `array` from being affected by these calls.

But that means that in this version:

  array = [1, 2, 3]
  return withCopy(array)
      ..remove(at: 1)
      ..remove(at: 0)

You are trying to call `mutating` methods on an *immutable* value, the return value of `withCopy`. Normally, the compiler would reject that.

Perhaps you could say that method cascades operate on a copy if the receiver is immutable, but that makes code vague and its behavior subtle and easily changed by accident. For instance, if a property is `internal private(set)`, then moving a method cascade from code which can't see the setter to code which can would silently change the code from immutable to mutable. Similarly, adding the `private(set)` would not cause the code which previously modified it to produce an error; it would instead silently change to no longer mutate where it used to before. That's not acceptable behavior from a language feature.

About the only solution to this I can come up with is to make `withCopy` have an `inout` return. But this at best forms an attractive nuisance: If you use normal `mutating` method calls instead of method cascading, your changes are going to disappear into the ether. And depending on how `inout` returns are actually implemented, it could lead to worse misbehavior.

--
Brent Royal-Gordon
Architechies

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

- 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.

I would not. It gives you a way to give a value type a short, scoped, immutable alias:

  with(RareMagicalDeviceOwner.shared.spimsterWickets[randomIndex]) {
    print($0.turns)
    print($0.turnSpeed)
  }

And in this form, there is no danger of mistakenly mutating the value type, because mutating methods would not be allowed:

  with(RareMagicalDeviceOwner.shared.spimsterWickets[randomIndex]) {
    $0.turnRepeatedly(times: 3) // Error: can't call mutating method on immutable parameter
  }

To be clear, I'm not convinced there's a need to make any change from the proposed version at all. I'm spitballing alternate designs here, trying to see if there might be something a little better out there. But so far, I think the proposal balances the feature size against strictness pretty well, whereas these stricter designs I'm discussing increase the surface of the feature more than they improve it. This is a small (but significant!) convenience, and I feel pretty strongly that it should have a small implementation.

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?

Yes, absolutely. Method cascades have a narrow use case: methods on `self`. Not everything in Swift is a method, and not all methods are on `self`.

  with(tableView.cellForRow(at: indexPath).myLabel) { label in
    print("Constraining label: \(label)")
    
    NSLayoutConstraint.activate(
      NSLayoutConstraint.withVisualFormat("|-[label]-|", options: , metrics: [:], views: ["label": label]) +
      NSLayoutConstraint.withVisualFormat("V:|[label]|", options: , metrics: [:], views: ["label": label])
    )
    
    constrainedLabels.append(label)
  }

None of the calls in that `with` block would benefit from method cascades, but they all benefit from `with`.

- 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.

I specifically considered and rejected `withCopy` because it only creates a copy of a value type, not a reference type. (Of course, it does create a copy of the reference itself, but that's a very subtle distinction.) I chose `withVar` to make it very clear that you're getting the same semantics as you would for a `var` temporary.

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()

Honestly, I'm not sure there's a coherent way to make method cascades work with your `withCopy` (or the `copy` function you mentioned upthread) at all.

Here's the problem. Suppose you have a property like this:

  var array: [Int]

And then you write this:

  array = [1, 2, 3]
  return array
      ..remove(at: 1)
      ..remove(at: 0)

I assume you think this should not only *return* `[3]`, but also *set* `array` to `[3]`. That's kind of implied by the fact that you think we need a `withCopy(array)` call to protect `array` from being affected by these calls.

But that means that in this version:

  array = [1, 2, 3]
  return withCopy(array)
      ..remove(at: 1)
      ..remove(at: 0)

You are trying to call `mutating` methods on an *immutable* value, the return value of `withCopy`. Normally, the compiler would reject that.

You are right, there would need to be an exception for method cascades. That might be a reasonable exception to make because we already know the temporary is not just the subject of mutation but also the result of the expression. The method cascade just operates on the temporary in-place before being used in the surrounding expression or statement .

Perhaps you could say that method cascades operate on a copy if the receiver is immutable

This isn’t necessary with the previously mentioned exception for allowing mutating method cascades on temporaries.

···

On May 27, 2016, at 7:19 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

, but that makes code vague and its behavior subtle and easily changed by accident. For instance, if a property is `internal private(set)`, then moving a method cascade from code which can't see the setter to code which can would silently change the code from immutable to mutable. Similarly, adding the `private(set)` would not cause the code which previously modified it to produce an error; it would instead silently change to no longer mutate where it used to before. That's not acceptable behavior from a language feature.

About the only solution to this I can come up with is to make `withCopy` have an `inout` return. But this at best forms an attractive nuisance: If you use normal `mutating` method calls instead of method cascading, your changes are going to disappear into the ether. And depending on how `inout` returns are actually implemented, it could lead to worse misbehavior.

--
Brent Royal-Gordon
Architechies

What are the differences or benefits of the above examples over the existing `do { let ... }` form?

    do {
      let w = RareMagicalDeviceOwner.shared.spimsterWickets[randomIndex]
      print(w.turns)
      print(w.turnSpeed)
    }

    do {
      let label = tableView.cellForRow(at: indexPath).myLabel
      print("Constraining label: \(label)")
      NSLayoutConstraint.activate(
        NSLayoutConstraint.withVisualFormat("|-[label]-|", options: , metrics: [:], views: ["label": label]) +
        NSLayoutConstraint.withVisualFormat("V:|[label]|", options: , metrics: [:], views: ["label": label])
      )
      constrainedLabels.append(label)
    }

···

On May 27, 2016, at 5:19 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

- 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.

I would not. It gives you a way to give a value type a short, scoped, immutable alias:

  with(RareMagicalDeviceOwner.shared.spimsterWickets[randomIndex]) {
    print($0.turns)
    print($0.turnSpeed)
  }

And in this form, there is no danger of mistakenly mutating the value type, because mutating methods would not be allowed:

  with(RareMagicalDeviceOwner.shared.spimsterWickets[randomIndex]) {
    $0.turnRepeatedly(times: 3) // Error: can't call mutating method on immutable parameter
  }

To be clear, I'm not convinced there's a need to make any change from the proposed version at all. I'm spitballing alternate designs here, trying to see if there might be something a little better out there. But so far, I think the proposal balances the feature size against strictness pretty well, whereas these stricter designs I'm discussing increase the surface of the feature more than they improve it. This is a small (but significant!) convenience, and I feel pretty strongly that it should have a small implementation.

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?

Yes, absolutely. Method cascades have a narrow use case: methods on `self`. Not everything in Swift is a method, and not all methods are on `self`.

  with(tableView.cellForRow(at: indexPath).myLabel) { label in
    print("Constraining label: \(label)")
    
    NSLayoutConstraint.activate(
      NSLayoutConstraint.withVisualFormat("|-[label]-|", options: , metrics: [:], views: ["label": label]) +
      NSLayoutConstraint.withVisualFormat("V:|[label]|", options: , metrics: [:], views: ["label": label])
    )
    
    constrainedLabels.append(label)
  }

None of the calls in that `with` block would benefit from method cascades, but they all benefit from `with`.

--
Greg Parker gparker@apple.com Runtime Wrangler

You are trying to call `mutating` methods on an *immutable* value, the return value of `withCopy`. Normally, the compiler would reject that.

You are right, there would need to be an exception for method cascades. That might be a reasonable exception to make because we already know the temporary is not just the subject of mutation but also the result of the expression. The method cascade just operates on the temporary in-place before being used in the surrounding expression or statement .

Perhaps you could say that method cascades operate on a copy if the receiver is immutable

This isn’t necessary with the previously mentioned exception for allowing mutating method cascades on temporaries.

I don't understand what you mean by "temporary". Are you saying that Swift should distinguish between things which could be mutable but don't happen to be (variables and subscripts), and things which cannot ever be mutable (functions and methods)? Will this distinction hold up when we get `inout`-returning functions?

Basically, what principled rule makes Swift treat this:

  immutableVariable..mutatingMethod()

And this:

  functionCall()..mutatingMethod()

Differently?

Or is the distinction between this:

  immutableVariable..mutatingMethod()

And this:

  mutableVariable..mutatingMethod()

And if so, how is that not going to lead to bugs when people move code around?

···

--
Brent Royal-Gordon
Architechies