[swift-evolution] Reconsidering SE-0003 Removing var from Function Parameters and Pattern Matching

Dany St-Amant dsa.mls at icloud.com
Fri Jan 29 06:25:23 CST 2016


Neither for or agains the if var, I just wanted to comment on the generic example used in this discussion.

The vast majority of the example provided in this discussion use variable shadowing, and a brief context
if var x = x {
    x =+ x
    print("x is \(x)")
}

It is easy for the original code writer to fully know that the mutation on x is not visible outside the if
if var x = x {
    x =+ x
    print("x is \(x)") // Modified copy
}
print("x is \(x)") // Original copy

We can even say that the future code maintainer will quickly get it due to the short span of the code.

But as the code grows:
if var x = x {
    // some 25 lines of implementation
    x =+ x
    print("x is \(x)")
}
print("x is \(x)")

For the code maintainer, the if var can then easily fall outside his visual range, so the code look like:
    x =+ x
    print("x is \(x)")
}
print("x is \(x)")

The reason as to why the two print display different result can then be quite a head scratcher.

Having support for if var without shadowing might have its merit:
if var y = x { /*  */ }
which need to be currently one as
if let tmp = x {
    var y = tmp
}

But it seems that either way, the presence/need of if var, encourage people to use variable shadowing. Shadowing is a required evil in the world of name clashing, but in this if var concept it is a really bad habit.

Dany


> Le 28 janv. 2016 à 16:45, Jarod Long via swift-evolution <swift-evolution at swift.org> a écrit :
> 
> 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 at swift.org <mailto:swift-evolution at 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 at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160129/d50c0684/attachment.html>


More information about the swift-evolution mailing list