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

Nate Birkholz nbirkholz at gmail.com
Fri Jan 22 12:05:12 CST 2016


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 at swift.org> wrote:

>
> > On Jan 22, 2016, at 9:26 AM, David Farler via swift-evolution <
> swift-evolution at 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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>



-- 
Nate Birkholz
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160122/acc4c1c2/attachment.html>


More information about the swift-evolution mailing list