Reconsidering SE-0003 Removing var from Function Parameters and Pattern Matching


(David Farler) #1

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the change in its entirety. After collecting feedback since Swift's open source launch, I no longer feel this is a good move and there are a few reasons why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses before and after the "Modify" part, before returning or reassigning with the new value.

I've seen a few common responses to the var removal. Consider a `Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
   var r = self
   r.origin = (x, y)
   return r
}
}

This is a much better solution than shadowing but you would need one of these for any property that you want to mutate and I think you'll agree that it doesn't scale with the language we have today. This response begs for a kind of initializer that takes all of the fields of the original struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on swift-evolution in the future, which would provide a clear alternative to direct mutation patterns. Even then, I think having complementary patterns in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a value as a peer in its own scope, not an inner scope like if statements. Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and reference semantics, who owns a value and when, how effects are propagated back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their semantics, mutating algorithms are written in a familiar style but keeping effects limited to your unique reference. I don't think we should give that up now to address confusion about semantics, out of principle, or in anticipation of new language features. I propose cancelling this change for Swift 3 and continue to allow `var` in the grammar everywhere it occurs in Swift 2.2.

Regards,
David


(Joe Groff) #2

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the change in its entirety. After collecting feedback since Swift's open source launch, I no longer feel this is a good move and there are a few reasons why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses before and after the "Modify" part, before returning or reassigning with the new value.

I've seen a few common responses to the var removal. Consider a `Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

These examples don't make sense to me. None of them mutate the 'if var rect' binding at all. Are you sure this is what you meant?

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
  var r = self
  r.origin = (x, y)
  return r
}
}

This is a much better solution than shadowing but you would need one of these for any property that you want to mutate and I think you'll agree that it doesn't scale with the language we have today. This response begs for a kind of initializer that takes all of the fields of the original struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

You can approximate this today without any new language features:

protocol Updatable {}
extension Updatable {
  func with<T>(change: (inout T) -> ()) -> T {
    var update = value
    change(&update)
    return update
  }
}

if let rect = selection?.rect.with { $0.origin = newOrigin } {
}

I think this approach generally leads to cleaner code, since it's not forcing you to bind names to otherwise uninteresting intermediate values.

Straw syntax, but maybe you'll see something along these lines on swift-evolution in the future, which would provide a clear alternative to direct mutation patterns. Even then, I think having complementary patterns in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a value as a peer in its own scope, not an inner scope like if statements. Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and reference semantics, who owns a value and when, how effects are propagated back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their semantics, mutating algorithms are written in a familiar style but keeping effects limited to your unique reference. I don't think we should give that up now to address confusion about semantics, out of principle, or in anticipation of new language features. I propose cancelling this change for Swift 3 and continue to allow `var` in the grammar everywhere it occurs in Swift 2.2.

I disagree. We have a lot of evidence that 'if var' confuses people—a lot of users think that 'if var' and 'var' bindings in case patterns will write back to the original value when this isn't the case. Classes make this worse, since 'if var' *will* seem to work that way when projecting through optional class references, and the value vs reference semantics divide is confusing enough as it is. Those of us who do understand the semantics don't save anything either—I at least have to mentally audit any code I see using 'if var' to ensure that writeback wasn't intended by the original author. Code is read and maintained more often than it's written, and it's written by more novices than experts, and 'if var' and its friends feel like very expert-writer-centric features to me.

-Joe

···

On Jan 22, 2016, at 9:26 AM, David Farler via swift-evolution <swift-evolution@swift.org> wrote:


(Chéyo Jiménez) #3

+1

I had filed SR-534 to add shadow variables so that code would compile. The
problem is more apparent when the code uses structs heavily, in my
mind, forcing only let binding, might even encourage the use of classes so
that the let binding works for mutation. In an if let binding I already use
the same name for the binding and adding a shadow variable makes code sort
of ugly and repetitive for value optional types.

If let foo = foo, bar = bar {
    var foo = foo // don't need for classes
    var bar = bar //
         foo.mutare()
         bar.mutate()
}

···

On Friday, January 22, 2016, David Farler via swift-evolution < swift-evolution@swift.org> wrote:

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the
change in its entirety. After collecting feedback since Swift's open source
launch, I no longer feel this is a good move and there are a few reasons
why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses
before and after the "Modify" part, before returning or reassigning with
the new value.

I've seen a few common responses to the var removal. Consider a
`Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be
impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
   var r = self
   r.origin = (x, y)
   return r
}
}

This is a much better solution than shadowing but you would need one of
these for any property that you want to mutate and I think you'll agree
that it doesn't scale with the language we have today. This response begs
for a kind of initializer that takes all of the fields of the original
struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on
swift-evolution in the future, which would provide a clear alternative to
direct mutation patterns. Even then, I think having complementary patterns
in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that
ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a
value as a peer in its own scope, not an inner scope like if statements.
Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and
reference semantics, who owns a value and when, how effects are propagated
back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their
semantics, mutating algorithms are written in a familiar style but keeping
effects limited to your unique reference. I don't think we should give that
up now to address confusion about semantics, out of principle, or in
anticipation of new language features. I propose cancelling this change for
Swift 3 and continue to allow `var` in the grammar everywhere it occurs in
Swift 2.2.

Regards,
David
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <javascript:;>
https://lists.swift.org/mailman/listinfo/swift-evolution


#4

+1

have a look with my library:
https://github.com/SusanDoggie/Doggie/tree/master/Doggie/Accelerate

public func Add<T: IntegerType>(count: Int, var _ left: UnsafePointer<T>, _
left_stride: Int, var _ right: UnsafePointer<T>, _ right_stride: Int, var _
output: UnsafeMutablePointer<T>, _ out_stride: Int) {

    for _ in 0..<count {

        output.memory = left.memory + right.memory

        left += left_stride

        right += right_stride

        output += out_stride

    }

}

There are lots of code moving pointers by modifying the shadow copy of
pointer type. This move will add plenty of redundant code lines.

···

2016-01-22 19:26 GMT+08:00 David Farler <dfarler@apple.com>:

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the
change in its entirety. After collecting feedback since Swift's open source
launch, I no longer feel this is a good move and there are a few reasons
why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses
before and after the "Modify" part, before returning or reassigning with
the new value.

I've seen a few common responses to the var removal. Consider a
`Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be
impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
   var r = self
   r.origin = (x, y)
   return r
}
}

This is a much better solution than shadowing but you would need one of
these for any property that you want to mutate and I think you'll agree
that it doesn't scale with the language we have today. This response begs
for a kind of initializer that takes all of the fields of the original
struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on
swift-evolution in the future, which would provide a clear alternative to
direct mutation patterns. Even then, I think having complementary patterns
in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that
ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a
value as a peer in its own scope, not an inner scope like if statements.
Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and
reference semantics, who owns a value and when, how effects are propagated
back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their
semantics, mutating algorithms are written in a familiar style but keeping
effects limited to your unique reference. I don't think we should give that
up now to address confusion about semantics, out of principle, or in
anticipation of new language features. I propose cancelling this change for
Swift 3 and continue to allow `var` in the grammar everywhere it occurs in
Swift 2.2.

Regards,
David


(Marc Knaup) #5

Scanning through our iOS project with ~600 Swift files we barely use `var`
for function parameters or for if/guard statements.

I think the problems you outline should not be solved by using `var` but by
making the code's intent much clearer by using distinct variable names.

In your example it is not clear what the purpose of the shadowed `rect`
variable is. The same is true if you use `if var rect = …`:

var selection = getRectangularSelection()
if let rect = selection?.rect {
  var rect = rect // what is rect used for? the variable name is quite
generic
  // mutate `rect` ...
  // probably a lot of code
  // …

  selection.rect = rect // what rect again?
}

A better solution is to name the variables differently and make their
intent very clear:

var selection = getRectangularSelection()
if let currentRect = selection?.rect {
  var expandedRect = currentRect // intent becomes clear now
  // expand `expandedRect` ...
  // probably a lot of code
  // …

  selection.rect = expandedRect // ah, THAT rect!
}

So `if var` is really not necessary and causes more harm than good due to
reduced clarity.

-1 for reversing the proposal from me.

···

On Fri, Jan 22, 2016 at 6:26 PM, David Farler via swift-evolution < swift-evolution@swift.org> wrote:

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the
change in its entirety. After collecting feedback since Swift's open source
launch, I no longer feel this is a good move and there are a few reasons
why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses
before and after the "Modify" part, before returning or reassigning with
the new value.

I've seen a few common responses to the var removal. Consider a
`Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be
impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
   var r = self
   r.origin = (x, y)
   return r
}
}

This is a much better solution than shadowing but you would need one of
these for any property that you want to mutate and I think you'll agree
that it doesn't scale with the language we have today. This response begs
for a kind of initializer that takes all of the fields of the original
struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on
swift-evolution in the future, which would provide a clear alternative to
direct mutation patterns. Even then, I think having complementary patterns
in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that
ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a
value as a peer in its own scope, not an inner scope like if statements.
Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and
reference semantics, who owns a value and when, how effects are propagated
back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their
semantics, mutating algorithms are written in a familiar style but keeping
effects limited to your unique reference. I don't think we should give that
up now to address confusion about semantics, out of principle, or in
anticipation of new language features. I propose cancelling this change for
Swift 3 and continue to allow `var` in the grammar everywhere it occurs in
Swift 2.2.

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


(Charles Srstka) #6

+1

Aside from forcing the use of temporary ‘let’ variables whose only purpose is to be immediately assigned to a var, I feel that “var works in these places where you can have let, but not these other places” is confusing. The more consistent the language is, the easier it is to wrap one’s head around. You may not see “if var” as often as “if let” in code, but if you understand what “if let” means, and what “var” means, it makes perfect sense what the combination of the two will do.

Charles

···

On Jan 22, 2016, at 11:26 AM, David Farler via swift-evolution <swift-evolution@swift.org> wrote:

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the change in its entirety. After collecting feedback since Swift's open source launch, I no longer feel this is a good move and there are a few reasons why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses before and after the "Modify" part, before returning or reassigning with the new value.

I've seen a few common responses to the var removal. Consider a `Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
  var r = self
  r.origin = (x, y)
  return r
}
}

This is a much better solution than shadowing but you would need one of these for any property that you want to mutate and I think you'll agree that it doesn't scale with the language we have today. This response begs for a kind of initializer that takes all of the fields of the original struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on swift-evolution in the future, which would provide a clear alternative to direct mutation patterns. Even then, I think having complementary patterns in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a value as a peer in its own scope, not an inner scope like if statements. Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and reference semantics, who owns a value and when, how effects are propagated back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their semantics, mutating algorithms are written in a familiar style but keeping effects limited to your unique reference. I don't think we should give that up now to address confusion about semantics, out of principle, or in anticipation of new language features. I propose cancelling this change for Swift 3 and continue to allow `var` in the grammar everywhere it occurs in Swift 2.2.

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


(Zachary Waldowski) #7

-1

Having already adopted the syntax in my projects in anticipation of 2.2,
the increase in clarity at the expense of terseness is appreciated. A
proposal should not be discussing an alternative, not a rollback.

Cheers!
Zachary Waldowski
zach@waldowski.me

···

On Fri, Jan 22, 2016, at 12:26 PM, David Farler via swift-evolution wrote:

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the
change in its entirety. After collecting feedback since Swift's open
source launch, I no longer feel this is a good move and there are a few
reasons why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the
uses before and after the "Modify" part, before returning or reassigning
with the new value.

I've seen a few common responses to the var removal. Consider a
`Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be
impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
   var r = self
   r.origin = (x, y)
   return r
}
}

This is a much better solution than shadowing but you would need one of
these for any property that you want to mutate and I think you'll agree
that it doesn't scale with the language we have today. This response begs
for a kind of initializer that takes all of the fields of the original
struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on
swift-evolution in the future, which would provide a clear alternative to
direct mutation patterns. Even then, I think having complementary
patterns in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that
ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a
value as a peer in its own scope, not an inner scope like if statements.
Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and
reference semantics, who owns a value and when, how effects are
propagated back to values, but I think we can attack the problem with
more finesse.

Value types are one of the attractive features of Swift – because of
their semantics, mutating algorithms are written in a familiar style but
keeping effects limited to your unique reference. I don't think we should
give that up now to address confusion about semantics, out of principle,
or in anticipation of new language features. I propose cancelling this
change for Swift 3 and continue to allow `var` in the grammar everywhere
it occurs in Swift 2.2.

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


(Lily Ballard) #8

Oh man, huge +1 from me here.

Apparently I never actually read that proposal. I was under the impression it only removed var from function parameters. And while I find that mildly annoying, I was ok with it because of the argument for removing as many keywords as possible from function parameter lists (e.g. to open up those keywords to be used as external parameter names). Though I'd really prefer to keep var anyway.

But I didn't realize until now that it also prohibits `var` from _all_ pattern matching, such as if-let, guard-let, etc. And I find that incredibly restrictive. I am not ok at all with that restriction. It makes no sense to me, and it makes a lot of very clean code become much messier by the addition of completely spurious `var foo = foo` lines everywhere. What's more, you can't even add lines like that everywhere because of Swift's prohibition from shadowing variables from the same scope (e.g. you can't shadow the variable bound from `guard let` because it's in the same scope).

I see Dave Abrahams arguing that this feature is tripping some people up. But I don't see that as appropriate grounds for removing it from the language. If people don't understand `if var` or `for var x in` then they can just not write code that uses that feature. And if the argument is that people might have to read other code that uses that, well, there's plenty of stuff in Swift that you have to actually learn about before you can understand how it works, and using `var` in patterns does not seem like one of the trickiest things. And if the confusion stems from not understanding the difference between `var` and `inout`, I don't see how preventing someone from writing `if var` will solve that confusion, because the same person would presumably have the same confusion about `var foo = bar`.

-Kevin Ballard

···

On Fri, Jan 22, 2016, at 09:26 AM, David Farler via swift-evolution wrote:

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the change in its entirety. After collecting feedback since Swift's open source launch, I no longer feel this is a good move and there are a few reasons why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses before and after the "Modify" part, before returning or reassigning with the new value.

I've seen a few common responses to the var removal. Consider a `Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
   var r = self
   r.origin = (x, y)
   return r
}
}

This is a much better solution than shadowing but you would need one of these for any property that you want to mutate and I think you'll agree that it doesn't scale with the language we have today. This response begs for a kind of initializer that takes all of the fields of the original struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on swift-evolution in the future, which would provide a clear alternative to direct mutation patterns. Even then, I think having complementary patterns in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a value as a peer in its own scope, not an inner scope like if statements. Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and reference semantics, who owns a value and when, how effects are propagated back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their semantics, mutating algorithms are written in a familiar style but keeping effects limited to your unique reference. I don't think we should give that up now to address confusion about semantics, out of principle, or in anticipation of new language features. I propose cancelling this change for Swift 3 and continue to allow `var` in the grammar everywhere it occurs in Swift 2.2.

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


(Tino) #9

Although I think "if var" should stay (probably just because of symmetry with "if let"), I have minor concerns with this proposal, as it brings up a more fundamental issue in the evolution process:
When is a decision final?

I guess it's hard to find the balance between discussing the same things over and over vs. stubborn insisting on a idea that is not as good as expected, but independent from this particular proposal, I think revising a decision should be done with caution, and that it is good that this discussion was started by a member of the Swift team…
Maybe it's possible to formalize the process so that an accepted proposal will not automatically be included in a future release, but rather has to pass a "veto-phase" after it has been implemented in a beta and people got a chance play with it (cancellation should stay an exception, though).

Back to the topic, I don't think "if var" and "guard var" brings more confusion than, for example, the difference between struct and class:
var and let always introduce a new value, and never change properties of something that already exists.
Additionally, I think
if let x = x {
  var x = x
looks rather ugly with all those x-assignments.
Thinking of my own source, I mostly use "if let" with return values of functions, so there is no confusion at all.

The argument of symmetry doesn't apply to function parameters, and I'm not really opposed to remove the "var-option" there — although afaics, most languages don't enforce const on parameters (which has the benefit that struct and class are treated the same way… and const has no advantage in a narrow scope)

Tino


(Nate Birkholz) #10

How *long* is it confusing, though? I feel like removing the utility and
especially the fluid expressiveness of "if var" or var parameters for
something that people will learn pretty quickly seems a bit spiteful to the
face.

···

On Fri, Jan 22, 2016 at 9:45 AM, Joe Groff via swift-evolution < swift-evolution@swift.org> wrote:

> On Jan 22, 2016, at 9:26 AM, David Farler via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Hello everyone,
>
> I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the
change in its entirety. After collecting feedback since Swift's open source
launch, I no longer feel this is a good move and there are a few reasons
why.
>
> There are two main patterns that the removal penalizes:
>
> - Get-Modify-Reassign
> - Get-Modify-Return
>
> I've found that many of the problems with this proposal stem from the
uses before and after the "Modify" part, before returning or reassigning
with the new value.
>
> I've seen a few common responses to the var removal. Consider a
`Rectangle` struct:
>
>
> struct Rectangle {
> var origin: (x: Double, y: Double)
> var size: (width: Double, height: Double)
> }
>
>
> Even with mutable variables `origin` and `size`, this pattern would be
impossible:
>
>
> var selection = getRectangularSelection()
> if var rect = selection?.rect {
> // Mutate `rect` ...
> selection.rect = rect
> }

These examples don't make sense to me. None of them mutate the 'if var
rect' binding at all. Are you sure this is what you meant?

>
>
> So, one might shadow the variable, which is not ideal:
>
>
> var selection = getRectangularSelection()
> if let rect = selection?.rect {
> var rect = rect // Not so great
> // Mutate `rect` ...
> selection.rect = rect
> }
>
>
> Or, you might make a transformation function on `Rect`:
>
>
> struct Rectangle {
> var origin: (x: Double, y: Double)
> var size: (width: Double, height: Double)
> func withOrigin(x: Double, y: Double) -> Rect {
> var r = self
> r.origin = (x, y)
> return r
> }
> }
>
>
> This is a much better solution than shadowing but you would need one of
these for any property that you want to mutate and I think you'll agree
that it doesn't scale with the language we have today. This response begs
for a kind of initializer that takes all of the fields of the original
struct except any that you want to override:
>
>
> if let rect = selection?.rect.with(origin: newOrigin) {
> // ...
> }

You can approximate this today without any new language features:

protocol Updatable {}
extension Updatable {
  func with<T>(change: (inout T) -> ()) -> T {
    var update = value
    change(&update)
    return update
  }
}

if let rect = selection?.rect.with { $0.origin = newOrigin } {
}

I think this approach generally leads to cleaner code, since it's not
forcing you to bind names to otherwise uninteresting intermediate values.

>
>
> Straw syntax, but maybe you'll see something along these lines on
swift-evolution in the future, which would provide a clear alternative to
direct mutation patterns. Even then, I think having complementary patterns
in the language isn't a bad thing.
>
> These problems come up with the other variable bindings but the one that
ended up bothering me the most was `guard var`:
>
>
> func transform(selection: Rect?) {
> guard let rect = selection else { return }
> var _rect = rect
> // Mutate `_rect` ...
> }
>
>
> One of the guard statement's main purposes is to conditionally bind a
value as a peer in its own scope, not an inner scope like if statements.
Not having var makes the guard statement much weaker.
>
> There is certainly a bit of confusion about the nuances between value
and reference semantics, who owns a value and when, how effects are
propagated back to values, but I think we can attack the problem with more
finesse.
>
> Value types are one of the attractive features of Swift – because of
their semantics, mutating algorithms are written in a familiar style but
keeping effects limited to your unique reference. I don't think we should
give that up now to address confusion about semantics, out of principle, or
in anticipation of new language features. I propose cancelling this change
for Swift 3 and continue to allow `var` in the grammar everywhere it occurs
in Swift 2.2.

I disagree. We have a lot of evidence that 'if var' confuses people—a lot
of users think that 'if var' and 'var' bindings in case patterns will write
back to the original value when this isn't the case. Classes make this
worse, since 'if var' *will* seem to work that way when projecting through
optional class references, and the value vs reference semantics divide is
confusing enough as it is. Those of us who do understand the semantics
don't save anything either—I at least have to mentally audit any code I see
using 'if var' to ensure that writeback wasn't intended by the original
author. Code is read and maintained more often than it's written, and it's
written by more novices than experts, and 'if var' and its friends feel
like very expert-writer-centric features to me.

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

--
Nate Birkholz


(Ben Langmuir) #11

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the change in its entirety. After collecting feedback since Swift's open source launch, I no longer feel this is a good move and there are a few reasons why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses before and after the "Modify" part, before returning or reassigning with the new value.

I've seen a few common responses to the var removal. Consider a `Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

These examples don't make sense to me. None of them mutate the 'if var rect' binding at all. Are you sure this is what you meant?

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
var r = self
r.origin = (x, y)
return r
}
}

This is a much better solution than shadowing but you would need one of these for any property that you want to mutate and I think you'll agree that it doesn't scale with the language we have today. This response begs for a kind of initializer that takes all of the fields of the original struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

You can approximate this today without any new language features:

protocol Updatable {}
extension Updatable {
func with<T>(change: (inout T) -> ()) -> T {
   var update = value
   change(&update)
   return update
}
}

if let rect = selection?.rect.with { $0.origin = newOrigin } {
}

I think this approach generally leads to cleaner code, since it's not forcing you to bind names to otherwise uninteresting intermediate values.

Straw syntax, but maybe you'll see something along these lines on swift-evolution in the future, which would provide a clear alternative to direct mutation patterns. Even then, I think having complementary patterns in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a value as a peer in its own scope, not an inner scope like if statements. Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and reference semantics, who owns a value and when, how effects are propagated back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their semantics, mutating algorithms are written in a familiar style but keeping effects limited to your unique reference. I don't think we should give that up now to address confusion about semantics, out of principle, or in anticipation of new language features. I propose cancelling this change for Swift 3 and continue to allow `var` in the grammar everywhere it occurs in Swift 2.2.

I disagree. We have a lot of evidence that 'if var' confuses people—a lot of users think that 'if var' and 'var' bindings in case patterns will write back to the original value when this isn't the case. Classes make this worse, since 'if var' *will* seem to work that way when projecting through optional class references, and the value vs reference semantics divide is confusing enough as it is.
Those of us who do understand the semantics don't save anything either—I at least have to mentally audit any code I see using 'if var' to ensure that writeback wasn't intended by the original author.

Anecdata: I also find myself auditing every non-trivial use of ‘var’.

···

On Jan 22, 2016, at 9:45 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 22, 2016, at 9:26 AM, David Farler via swift-evolution <swift-evolution@swift.org> wrote:

Code is read and maintained more often than it's written, and it's written by more novices than experts, and 'if var' and its friends feel like very expert-writer-centric features to me.

-Joe
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Farler) #12

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the change in its entirety. After collecting feedback since Swift's open source launch, I no longer feel this is a good move and there are a few reasons why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses before and after the "Modify" part, before returning or reassigning with the new value.

I've seen a few common responses to the var removal. Consider a `Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

These examples don't make sense to me. None of them mutate the 'if var rect' binding at all. Are you sure this is what you meant?

I abbreviated there with // Mutate `rect.

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
var r = self
r.origin = (x, y)
return r
}
}

This is a much better solution than shadowing but you would need one of these for any property that you want to mutate and I think you'll agree that it doesn't scale with the language we have today. This response begs for a kind of initializer that takes all of the fields of the original struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

You can approximate this today without any new language features:

protocol Updatable {}
extension Updatable {
func with<T>(change: (inout T) -> ()) -> T {
   var update = value
   change(&update)
   return update
}
}

if let rect = selection?.rect.with { $0.origin = newOrigin } {
}

I think this approach generally leads to cleaner code, since it's not forcing you to bind names to otherwise uninteresting intermediate values.

It's definitely more explicit but I don't know if I agree that it's cleaner with respect to the mutations themselves – it has a lot of the same shape. I also worry about performance with this pattern as is.

Straw syntax, but maybe you'll see something along these lines on swift-evolution in the future, which would provide a clear alternative to direct mutation patterns. Even then, I think having complementary patterns in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a value as a peer in its own scope, not an inner scope like if statements. Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and reference semantics, who owns a value and when, how effects are propagated back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their semantics, mutating algorithms are written in a familiar style but keeping effects limited to your unique reference. I don't think we should give that up now to address confusion about semantics, out of principle, or in anticipation of new language features. I propose cancelling this change for Swift 3 and continue to allow `var` in the grammar everywhere it occurs in Swift 2.2.

I disagree. We have a lot of evidence that 'if var' confuses people—a lot of users think that 'if var' and 'var' bindings in case patterns will write back to the original value when this isn't the case. Classes make this worse, since 'if var' *will* seem to work that way when projecting through optional class references, and the value vs reference semantics divide is confusing enough as it is.

I totally agree that the subtlety of the semantics easily gets lost but I think we can come up with better ways to make them clearer without blowing away a whole class of syntax. I see this confusion as more of a holistic indictment of how we express the semantics rather than just the syntax of `var`. I just don't feel like removing this now is a slam dunk.

···

On Jan 22, 2016, at 9:45 AM, Joe Groff <jgroff@apple.com> wrote:

On Jan 22, 2016, at 9:26 AM, David Farler via swift-evolution <swift-evolution@swift.org> wrote:

Those of us who do understand the semantics don't save anything either—I at least have to mentally audit any code I see using 'if var' to ensure that writeback wasn't intended by the original author. Code is read and maintained more often than it's written, and it's written by more novices than experts, and 'if var' and its friends feel like very expert-writer-centric features to me.

-Joe


(Chéyo Jiménez) #13

if let currentRect = selection?.rect {
  var expandedRect = currentRect

VS

if var expandedRec = selection?.rect {

If let is perfectly fine if most of the code base is with classes. Perhaps
I can see the argument on why pattern matching and even why functions
should only allow let but for guard and if binding, the uses of var are
more practical and less boiler plate specially when dealing with mutable
value types.

···

On Saturday, January 23, 2016, Marc Knaup via swift-evolution < swift-evolution@swift.org> wrote:

Scanning through our iOS project with ~600 Swift files we barely use `var`
for function parameters or for if/guard statements.

I think the problems you outline should not be solved by using `var` but
by making the code's intent much clearer by using distinct variable names.

In your example it is not clear what the purpose of the shadowed `rect`
variable is. The same is true if you use `if var rect = …`:

var selection = getRectangularSelection()
if let rect = selection?.rect {
  var rect = rect // what is rect used for? the variable name is quite
generic
  // mutate `rect` ...
  // probably a lot of code
  // …

  selection.rect = rect // what rect again?
}

A better solution is to name the variables differently and make their
intent very clear:

var selection = getRectangularSelection()
if let currentRect = selection?.rect {
  var expandedRect = currentRect // intent becomes clear now
  // expand `expandedRect` ...
  // probably a lot of code
  // …

  selection.rect = expandedRect // ah, THAT rect!
}

So `if var` is really not necessary and causes more harm than good due to
reduced clarity.

-1 for reversing the proposal from me.

On Fri, Jan 22, 2016 at 6:26 PM, David Farler via swift-evolution < > swift-evolution@swift.org > <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>> wrote:

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the
change in its entirety. After collecting feedback since Swift's open source
launch, I no longer feel this is a good move and there are a few reasons
why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the
uses before and after the "Modify" part, before returning or reassigning
with the new value.

I've seen a few common responses to the var removal. Consider a
`Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be
impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
   var r = self
   r.origin = (x, y)
   return r
}
}

This is a much better solution than shadowing but you would need one of
these for any property that you want to mutate and I think you'll agree
that it doesn't scale with the language we have today. This response begs
for a kind of initializer that takes all of the fields of the original
struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on
swift-evolution in the future, which would provide a clear alternative to
direct mutation patterns. Even then, I think having complementary patterns
in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that
ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a
value as a peer in its own scope, not an inner scope like if statements.
Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and
reference semantics, who owns a value and when, how effects are propagated
back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of
their semantics, mutating algorithms are written in a familiar style but
keeping effects limited to your unique reference. I don't think we should
give that up now to address confusion about semantics, out of principle, or
in anticipation of new language features. I propose cancelling this change
for Swift 3 and continue to allow `var` in the grammar everywhere it occurs
in Swift 2.2.

Regards,
David
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
<javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Owens II) #14

Removing var from function params, I get that. But the if-binding is unfortunate. It's literally a line of code for the sole purpose of making the compiler happy.

Working with optionals is already clumsy enough, we really don't need to make it more so.

-David

···

On Jan 23, 2016, at 5:03 AM, J. Cheyo Jimenez via swift-evolution <swift-evolution@swift.org> wrote:

if let currentRect = selection?.rect {
  var expandedRect = currentRect

VS

if var expandedRec = selection?.rect {

If let is perfectly fine if most of the code base is with classes. Perhaps I can see the argument on why pattern matching and even why functions should only allow let but for guard and if binding, the uses of var are more practical and less boiler plate specially when dealing with mutable value types.

On Saturday, January 23, 2016, Marc Knaup via swift-evolution <swift-evolution@swift.org> wrote:
Scanning through our iOS project with ~600 Swift files we barely use `var` for function parameters or for if/guard statements.

I think the problems you outline should not be solved by using `var` but by making the code's intent much clearer by using distinct variable names.

In your example it is not clear what the purpose of the shadowed `rect` variable is. The same is true if you use `if var rect = …`:

var selection = getRectangularSelection()
if let rect = selection?.rect {
  var rect = rect // what is rect used for? the variable name is quite generic
  // mutate `rect` ...
  // probably a lot of code
  // …

  selection.rect = rect // what rect again?
}

A better solution is to name the variables differently and make their intent very clear:

var selection = getRectangularSelection()
if let currentRect = selection?.rect {
  var expandedRect = currentRect // intent becomes clear now
  // expand `expandedRect` ...
  // probably a lot of code
  // …

  selection.rect = expandedRect // ah, THAT rect!
}

So `if var` is really not necessary and causes more harm than good due to reduced clarity.

-1 for reversing the proposal from me.

On Fri, Jan 22, 2016 at 6:26 PM, David Farler via swift-evolution <swift-evolution@swift.org> wrote:
Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the change in its entirety. After collecting feedback since Swift's open source launch, I no longer feel this is a good move and there are a few reasons why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses before and after the "Modify" part, before returning or reassigning with the new value.

I've seen a few common responses to the var removal. Consider a `Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
   var r = self
   r.origin = (x, y)
   return r
}
}

This is a much better solution than shadowing but you would need one of these for any property that you want to mutate and I think you'll agree that it doesn't scale with the language we have today. This response begs for a kind of initializer that takes all of the fields of the original struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on swift-evolution in the future, which would provide a clear alternative to direct mutation patterns. Even then, I think having complementary patterns in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a value as a peer in its own scope, not an inner scope like if statements. Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and reference semantics, who owns a value and when, how effects are propagated back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their semantics, mutating algorithms are written in a familiar style but keeping effects limited to your unique reference. I don't think we should give that up now to address confusion about semantics, out of principle, or in anticipation of new language features. I propose cancelling this change for Swift 3 and continue to allow `var` in the grammar everywhere it occurs in Swift 2.2.

Regards,
David
_______________________________________________
swift-evolution mailing list
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


(David Farler) #15

An alternative would certainly be interesting but I would prefer to take it one step at a time and avoid being hasty so we can come up with something really great. What did most of your var fixes look like, by the way? Did you end up changing the layout of your value types or did you decide to add more vars?

David

···

On Jan 24, 2016, at 7:19 PM, Zach Waldowski via swift-evolution <swift-evolution@swift.org> wrote:

-1

Having already adopted the syntax in my projects in anticipation of 2.2,
the increase in clarity at the expense of terseness is appreciated. A
proposal should not be discussing an alternative, not a rollback.

Cheers!
Zachary Waldowski
zach@waldowski.me

On Fri, Jan 22, 2016, at 12:26 PM, David Farler via swift-evolution > wrote:

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the
change in its entirety. After collecting feedback since Swift's open
source launch, I no longer feel this is a good move and there are a few
reasons why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the
uses before and after the "Modify" part, before returning or reassigning
with the new value.

I've seen a few common responses to the var removal. Consider a
`Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be
impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
  var r = self
  r.origin = (x, y)
  return r
}
}

This is a much better solution than shadowing but you would need one of
these for any property that you want to mutate and I think you'll agree
that it doesn't scale with the language we have today. This response begs
for a kind of initializer that takes all of the fields of the original
struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on
swift-evolution in the future, which would provide a clear alternative to
direct mutation patterns. Even then, I think having complementary
patterns in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that
ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a
value as a peer in its own scope, not an inner scope like if statements.
Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and
reference semantics, who owns a value and when, how effects are
propagated back to values, but I think we can attack the problem with
more finesse.

Value types are one of the attractive features of Swift – because of
their semantics, mutating algorithms are written in a familiar style but
keeping effects limited to your unique reference. I don't think we should
give that up now to address confusion about semantics, out of principle,
or in anticipation of new language features. I propose cancelling this
change for Swift 3 and continue to allow `var` in the grammar everywhere
it occurs in Swift 2.2.

Regards,
David
_______________________________________________
swift-evolution mailing list
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


(Brent Royal-Gordon) #16

We have a lot of evidence that 'if var' confuses people—a lot of users think that 'if var' and 'var' bindings in case patterns will write back to the original value when this isn't the case.

Can we address this with a diagnostic?

  var bar: Int? = 1
  if var foo = bar {
    foo += 1 // Warning: Value is never used after modification (foo is a copy, not an alias)
  }
  print(bar)

···

--
Brent Royal-Gordon
Architechies


(Jarod Long) #17

I agree completely with Kevin. It doesn't seem intuitive to me that var in parameters and pattern matching is confusing. I actually see it the opposite way -- "why can I use var here but not there?". Allowing var wherever let can be used increases consistency, which is rarely a bad thing.

I checked for functions that use var parameters in one of my larger projects and found 29 cases (roughly half of which are implementations of arithmetic operators for custom types, which is an area that benefits significantly from this feature). Most of those cases are functions that return a mutated version of a parameter, which I find to be a common and useful pattern. Less common, but still valuable, are cases where it's convenient and logical to track some internal state in one of the function's parameters. For example, a function that draws a list of items of variable height starting at some Y position passed as a parameter can naturally calculate the Y position of the next item by adding each item's height to the initial Y value as it is rendered.

As someone mentioned earlier, I also think that this is a pretty unfortunate pattern:

if let x = x {
  var x = x

Now there are three x's -- an optional one, an immutable one, and a mutable one, all with the same name. That seems to me to create more possibility for confusion than if var does. You can give each one a different name, but I've found that more often than not, it's very difficult to come up with non-arbitrary distinct names in these kinds of situations, and using names like optionalX or mutableX is so off-putting as to be out of the question for me personally.

I agree that it's important to always weigh a feature's value vs. its complexity, but I believe this is a case where the value justifies the complexity.

Jarod

···

On Jan 28, 2016, at 11:19, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

Oh man, huge +1 from me here.

Apparently I never actually read that proposal. I was under the impression it only removed var from function parameters. And while I find that mildly annoying, I was ok with it because of the argument for removing as many keywords as possible from function parameter lists (e.g. to open up those keywords to be used as external parameter names). Though I'd really prefer to keep var anyway.

But I didn't realize until now that it also prohibits `var` from _all_ pattern matching, such as if-let, guard-let, etc. And I find that incredibly restrictive. I am not ok at all with that restriction. It makes no sense to me, and it makes a lot of very clean code become much messier by the addition of completely spurious `var foo = foo` lines everywhere. What's more, you can't even add lines like that everywhere because of Swift's prohibition from shadowing variables from the same scope (e.g. you can't shadow the variable bound from `guard let` because it's in the same scope).

I see Dave Abrahams arguing that this feature is tripping some people up. But I don't see that as appropriate grounds for removing it from the language. If people don't understand `if var` or `for var x in` then they can just not write code that uses that feature. And if the argument is that people might have to read other code that uses that, well, there's plenty of stuff in Swift that you have to actually learn about before you can understand how it works, and using `var` in patterns does not seem like one of the trickiest things. And if the confusion stems from not understanding the difference between `var` and `inout`, I don't see how preventing someone from writing `if var` will solve that confusion, because the same person would presumably have the same confusion about `var foo = bar`.

-Kevin Ballard

On Fri, Jan 22, 2016, at 09:26 AM, David Farler via swift-evolution wrote:

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the change in its entirety. After collecting feedback since Swift's open source launch, I no longer feel this is a good move and there are a few reasons why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses before and after the "Modify" part, before returning or reassigning with the new value.

I've seen a few common responses to the var removal. Consider a `Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
  var r = self
  r.origin = (x, y)
  return r
}
}

This is a much better solution than shadowing but you would need one of these for any property that you want to mutate and I think you'll agree that it doesn't scale with the language we have today. This response begs for a kind of initializer that takes all of the fields of the original struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on swift-evolution in the future, which would provide a clear alternative to direct mutation patterns. Even then, I think having complementary patterns in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a value as a peer in its own scope, not an inner scope like if statements. Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and reference semantics, who owns a value and when, how effects are propagated back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their semantics, mutating algorithms are written in a familiar style but keeping effects limited to your unique reference. I don't think we should give that up now to address confusion about semantics, out of principle, or in anticipation of new language features. I propose cancelling this change for Swift 3 and continue to allow `var` in the grammar everywhere it occurs in Swift 2.2.

Regards,
David
_______________________________________________
swift-evolution mailing list
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


(Dave Abrahams) #18

Oh man, huge +1 from me here.

Apparently I never actually read that proposal. I was under the
impression it only removed var from function parameters. And while I
find that mildly annoying, I was ok with it because of the argument
for removing as many keywords as possible from function parameter
lists (e.g. to open up those keywords to be used as external parameter
names). Though I'd really prefer to keep var anyway.

But I didn't realize until now that it also prohibits `var` from _all_
pattern matching, such as if-let, guard-let, etc. And I find that
incredibly restrictive. I am not ok at all with that restriction. It
makes no sense to me, and it makes a lot of very clean code become
much messier by the addition of completely spurious `var foo = foo`
lines everywhere. What's more, you can't even add lines like that
everywhere because of Swift's prohibition from shadowing variables
from the same scope (e.g. you can't shadow the variable bound from
`guard let` because it's in the same scope).

I see Dave Abrahams arguing that this feature is tripping some people
up.

I'm really making others' arguments for them here. It is a fact that it
has tripped some people up. I still don't support removing it.

···

on Thu Jan 28 2016, Kevin Ballard <swift-evolution@swift.org> wrote:

But I don't see that as appropriate grounds for removing it from the
language. If people don't understand `if var` or `for var x in` then
they can just not write code that uses that feature. And if the
argument is that people might have to read other code that uses that,
well, there's plenty of stuff in Swift that you have to actually learn
about before you can understand how it works, and using `var` in
patterns does not seem like one of the trickiest things. And if the
confusion stems from not understanding the difference between `var`
and `inout`, I don't see how preventing someone from writing `if var`
will solve that confusion, because the same person would presumably
have the same confusion about `var foo = bar`.

-Kevin Ballard

On Fri, Jan 22, 2016, at 09:26 AM, David Farler via swift-evolution wrote:

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the change in its entirety. After collecting feedback since Swift's open source launch, I no longer feel this is a good move and there are a few reasons why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses before and after the "Modify" part, before returning or reassigning with the new value.

I've seen a few common responses to the var removal. Consider a `Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
   var r = self
   r.origin = (x, y)
   return r
}
}

This is a much better solution than shadowing but you would need one of these for any property that you want to mutate and I think you'll agree that it doesn't scale with the language we have today. This response begs for a kind of initializer that takes all of the fields of the original struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on swift-evolution in the future, which would provide a clear alternative to direct mutation patterns. Even then, I think having complementary patterns in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a value as a peer in its own scope, not an inner scope like if statements. Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and reference semantics, who owns a value and when, how effects are propagated back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their semantics, mutating algorithms are written in a familiar style but keeping effects limited to your unique reference. I don't think we should give that up now to address confusion about semantics, out of principle, or in anticipation of new language features. I propose cancelling this change for Swift 3 and continue to allow `var` in the grammar everywhere it occurs in Swift 2.2.

Regards,
David
_______________________________________________
swift-evolution mailing list
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

--
-Dave


(David Farler) #19

Thanks for your feedback, Kevin. As it stands, for this proposal, I think that there is merit in making function parameters only either `let` or `inout` because of var/inout confusion but it doesn't map well onto the other kinds of pattern matching, IMO.

David

···

On Jan 28, 2016, at 11:19 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

Oh man, huge +1 from me here.

Apparently I never actually read that proposal. I was under the impression it only removed var from function parameters. And while I find that mildly annoying, I was ok with it because of the argument for removing as many keywords as possible from function parameter lists (e.g. to open up those keywords to be used as external parameter names). Though I'd really prefer to keep var anyway.

But I didn't realize until now that it also prohibits `var` from _all_ pattern matching, such as if-let, guard-let, etc. And I find that incredibly restrictive. I am not ok at all with that restriction. It makes no sense to me, and it makes a lot of very clean code become much messier by the addition of completely spurious `var foo = foo` lines everywhere. What's more, you can't even add lines like that everywhere because of Swift's prohibition from shadowing variables from the same scope (e.g. you can't shadow the variable bound from `guard let` because it's in the same scope).

I see Dave Abrahams arguing that this feature is tripping some people up. But I don't see that as appropriate grounds for removing it from the language. If people don't understand `if var` or `for var x in` then they can just not write code that uses that feature. And if the argument is that people might have to read other code that uses that, well, there's plenty of stuff in Swift that you have to actually learn about before you can understand how it works, and using `var` in patterns does not seem like one of the trickiest things. And if the confusion stems from not understanding the difference between `var` and `inout`, I don't see how preventing someone from writing `if var` will solve that confusion, because the same person would presumably have the same confusion about `var foo = bar`.

-Kevin Ballard

On Fri, Jan 22, 2016, at 09:26 AM, David Farler via swift-evolution wrote:

Hello everyone,

I'd like to reconsider SE-0003 for Swift 3 and propose cancelling the change in its entirety. After collecting feedback since Swift's open source launch, I no longer feel this is a good move and there are a few reasons why.

There are two main patterns that the removal penalizes:

- Get-Modify-Reassign
- Get-Modify-Return

I've found that many of the problems with this proposal stem from the uses before and after the "Modify" part, before returning or reassigning with the new value.

I've seen a few common responses to the var removal. Consider a `Rectangle` struct:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
}

Even with mutable variables `origin` and `size`, this pattern would be impossible:

var selection = getRectangularSelection()
if var rect = selection?.rect {
// Mutate `rect` ...
selection.rect = rect
}

So, one might shadow the variable, which is not ideal:

var selection = getRectangularSelection()
if let rect = selection?.rect {
var rect = rect // Not so great
// Mutate `rect` ...
selection.rect = rect
}

Or, you might make a transformation function on `Rect`:

struct Rectangle {
var origin: (x: Double, y: Double)
var size: (width: Double, height: Double)
func withOrigin(x: Double, y: Double) -> Rect {
  var r = self
  r.origin = (x, y)
  return r
}
}

This is a much better solution than shadowing but you would need one of these for any property that you want to mutate and I think you'll agree that it doesn't scale with the language we have today. This response begs for a kind of initializer that takes all of the fields of the original struct except any that you want to override:

if let rect = selection?.rect.with(origin: newOrigin) {
// ...
}

Straw syntax, but maybe you'll see something along these lines on swift-evolution in the future, which would provide a clear alternative to direct mutation patterns. Even then, I think having complementary patterns in the language isn't a bad thing.

These problems come up with the other variable bindings but the one that ended up bothering me the most was `guard var`:

func transform(selection: Rect?) {
guard let rect = selection else { return }
var _rect = rect
// Mutate `_rect` ...
}

One of the guard statement's main purposes is to conditionally bind a value as a peer in its own scope, not an inner scope like if statements. Not having var makes the guard statement much weaker.

There is certainly a bit of confusion about the nuances between value and reference semantics, who owns a value and when, how effects are propagated back to values, but I think we can attack the problem with more finesse.

Value types are one of the attractive features of Swift – because of their semantics, mutating algorithms are written in a familiar style but keeping effects limited to your unique reference. I don't think we should give that up now to address confusion about semantics, out of principle, or in anticipation of new language features. I propose cancelling this change for Swift 3 and continue to allow `var` in the grammar everywhere it occurs in Swift 2.2.

Regards,
David
_______________________________________________
swift-evolution mailing list
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


(Douglas Gregor) #20

We certainly don’t want open-ended rehashing of past decisions, and I’d like to have the baseline rule be something very close to “the core team’s decisions are final” both to prevent such rehashing and also to emphasize the seriousness of the public review process: the public review is the point at which we need to gather widespread feedback on the direction of the language. Determining that there is a problem “after we shipped it” is a failure of the evolution process.

The evolution process has a number of stages (idea/proposal draft/public review/core team), where each new stage brings additional scrutiny of the proposal. The hope is that this scrutiny is enough to prevent us from making poor decisions that may need to be overturned, and that the swift-evolution community is representative enough of the larger Swift community to provide that scrutiny. SE-0003 is somewhat special because it’s one of a few changes for Swift 3 that were decided prior to open-sourcing Swift: it didn’t go through the whole evolution process, so it didn’t have as many eyes on it as language changes do now.

So, overall, I’d say that the core team’s decisions should be considered effectively final. If something gets through the entire evolution process and then we later find out it was a bad decision—e.g., due to massive negative feedback when it reaches the wider Swift community or unforeseen difficulties in implementation/rollout/etc.—the core team could bring up the idea of backing out the change. However, the evolution process *should* prevent this.

  - Doug

···

On Jan 28, 2016, at 4:40 AM, Tino Heth via swift-evolution <swift-evolution@swift.org> wrote:

Although I think "if var" should stay (probably just because of symmetry with "if let"), I have minor concerns with this proposal, as it brings up a more fundamental issue in the evolution process:
When is a decision final?

I guess it's hard to find the balance between discussing the same things over and over vs. stubborn insisting on a idea that is not as good as expected, but independent from this particular proposal, I think revising a decision should be done with caution, and that it is good that this discussion was started by a member of the Swift team…
Maybe it's possible to formalize the process so that an accepted proposal will not automatically be included in a future release, but rather has to pass a "veto-phase" after it has been implemented in a beta and people got a chance play with it (cancellation should stay an exception, though).