[swift-evolution] Obsoleting `if let`

Sean Heber sean at fifthace.com
Wed Feb 3 22:59:05 CST 2016


An idea that I've been kicking around would be to to replace if-(let/var/case)-where, guard-(let/var/case)-where with:

when <pattern> where <condition> {}
guard when <pattern> where <condition> else {}
if <condition> {} else {}
guard <condition> else {}

This seems pretty similar to the original design that Chris mentioned and which was rejected, but without it living inside of the "if-case" construct - instead "when" exists as its own kind of statement so "if" and "guard" can revert to a more standard form without binding / pattern matching.

"When" statement's patterns would allow some new variations to try to solve the pain points:

let foo: String?
var bar: String? = "thing"

when let exists = foo where exists == "hi" {
	// "foo" is still optional, not shadowed, "exists" only valid in this scope
}

when foo where foo == "hi" {
	// "foo" is a shadowed "let" because "foo" was originally a "let"
}

when bar where bar == "thing" {
 	// "bar" is modeled like an inout and is mutable here because "bar" was a "var"
	// so changes to "bar" here will be set back to the outer "bar" when the scope ends 
}

when var exists = foo {
	// "exists" is mutable local copy and only exists in this scope
}

when var bar = bar {
	// "bar" is a mutable shadowed copy of the outer "bar" and only exists in this scope
}

when case .thing(associated: value) = someEnumProperty {
	print(value)
}

All of these patterns would also apply to the guard variants as well with the obvious changes to the scoping.

I hope this isn't similar to the bind thread discussions since I haven't had time to keep up with everything there, but thought I'd toss it out here since it seemed to be on topic-ish.

l8r
Sean

Sent from my iPad

> On Feb 3, 2016, at 10:41 PM, Taras Zakharko via swift-evolution <swift-evolution at swift.org> wrote:
> 
> 
>> On 04 Feb 2016, at 05:03, Jonathan Tang <jonathan.d.tang at gmail.com> wrote:
>> 
>> FWIW, I don't see any problems with the current "if let x = ... " syntax or behavior.  I view it as a mainstream language finally implementing anaphoric-if (as described in Paul Graham's _On Lisp_), and the unwrapping seems like a natural consequence of the semantics, because what else *could* it do?  The if-statement needs to test on something; it seems natural to me that the if tests for optional == nil, and then the let binds the payload of the optional if it matches.
> 
> To be honest, I agree. But the bind threads reveals that many consider the current if let less transparent, so it makes sense to discuss alternatives.
> 
> One problem I can see with privileging optionals binding in conditional statements however,  is the inability to bind non-optionals. That would be quite useful sometimes. E.g. in conditionals:
> 
>   if let x = optional_value, y = some_fun_returning_non_optional() where x > y {}
> 
> forcing one to write something like this instead (or a nested if)
> 
>   myif: if x = optional_value {
>     let y = some_fun_returning_non_optional()
>     guard x > y else {break myif}
>     ...
>  }
> 
> Same goes for guard, it would be nice to do something like
> 
>   guard let 
>       x = optional1,
>       y = x.optional2,
>       z = x.non_optional,
>       w = z.optional3
>   else {
>     // failed to initialise 
>  }
> 
> If I remember correctly, I encountered this problem when playing with Metal, where I wanted to initialise a bunch of relevant variables in one go, but had to break up my guards into a number of blocks. This breaks the program logic. 
> 
> — Taras
> 
>> 
>> I wouldn't rule out there being something better, but I'm -1 on all the proposals I've seen so far to change it.
>> 
>>> On Wed, Feb 3, 2016 at 7:49 PM, Taras Zakharko via swift-evolution <swift-evolution at swift.org> wrote:
>>> I already suggested this in the bind thread but I guess it was either not interesting or people missed it, so here it goes again :)
>>> 
>>> What about changing the syntax of optional binding such that the optional unwrapping becomes explicit? I.e. instead of 
>>> 
>>>   if let x = some_optional { }
>>> 
>>> one writes
>>> 
>>>   if let x = some_optional! { }
>>> 
>>> Essentially, the point of this suggestion is that the runtime error generated by unwrapping an empty Optional is instead treated as condition failure in a conditional statement. While there is some typing overhead over the current syntax, I see a number of potential benefits of this approach:
>>> 
>>>  1. It is in line with the current semantics and syntax of optional unwrapping (does not introduce any new syntagm)
>>>  2. It makes the unwrapping operation explicit (thus addressing the basic criticism from the bind discussion)
>>>  3. It frees variable declaration of the contextual polisemy (i.e. let and var have the same semantics as nowhere else, there is no ‘unwrapping’ magic)
>>>  4. The change is minimal compare to what we have now and can be easily ported automatically
>>> 
>>> Potential issues:
>>> 
>>>  1. One character typing overhead — but I dot think that should matter. I always had the impression that Swift favours clarity over compactness (which is a good thing IMO)
>>>  2. it allows syntactic ambiguity with optional chaining. E.g. if let x = a.b?.c! { } and if let x = a.b!.c would have the same meaning. Then again, this ambiguity already exits in the language to begin with. 
>>> 
>>> — Taras
>>> 
>>>>> On 04 Feb 2016, at 01:25, Chris Lattner via swift-evolution <swift-evolution at swift.org> wrote:
>>>>> 
>>>>> 
>>>>> On Feb 3, 2016, at 3:47 PM, Jordan Rose via swift-evolution <swift-evolution at swift.org> wrote:
>>>>> 
>>>>> Data point (which Chris brought up already, I think?): We tried this* and got a lot of negative feedback. Optionals are unwrapped too often for people to be comfortable writing "if let name? = optionalCondition”.
>>>> 
>>>> Yes, I even implemented this and it was in the compiler for awhile, then later ripped it back out.  You can find the history in git.  I would guess that this all happened in ~March 2015.
>>>> 
>>>> -Chris
>>>> 
>>>>> 
>>>>> It may be more uniform and even more pedantically correct, but our users hated it.
>>>>> 
>>>>> Jordan
>>>>> 
>>>>> * The actual thing we tried only allowed patterns that began with 'let', but that's close enough.
>>>>> 
>>>>> 
>>>>>> On Feb 3, 2016, at 15:36, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org> wrote:
>>>>>> 
>>>>>> This is a continuation of and alternative proposal to "The bind thread", which seems to have petered out without consensus.
>>>>>> 
>>>>>> Currently there are three forms of `if` statement (and `guard` and `while`, but for simplicity I'll just say `if` throughout this discussion):
>>>>>> 
>>>>>> 	if booleanCondition
>>>>>> 	if let name = optionalCondition
>>>>>> 	if case pattern = expression
>>>>>> 
>>>>>> The boolean condition form is fine, but there are flaws in the other two. `if let` is unprincipled and doesn't really say what it does; `if case` is bulky and rarely used.* 
>>>>>> 
>>>>>> One very interesting thing about `if case`, too, is that it can actually do optional unwrapping:
>>>>>> 
>>>>>> 	if case let name? = optionalCondition
>>>>>> 
>>>>>> This avoids the problems with `if let`—it's principled (it comes from a larger language feature) and it explicitly says it's handling optionality—but it still runs up against `if case`'s rarity and wordiness.
>>>>>> 
>>>>>> So what I suggest is that we drop the `if let` form entirely and then drop the `case` keyword from `if case`. Pattern-matching conditions can still be distinguished from boolean conditions because boolean conditions can't contain an `=` operator. This, there would now only be two forms of if:
>>>>>> 
>>>>>> 	if booleanCondition
>>>>>> 	if pattern = expression
>>>>>> 
>>>>>> And the current `if let` is handled elegantly and clearly by existing pattern-matching shorthand, with only one additional character needed:
>>>>>> 
>>>>>> 	if let name? = optionalCondition
>>>>>> 
>>>>>> I see two complications with this.
>>>>>> 
>>>>>> The first is that, naively, `if let foo = bar` would still be valid, but would have different and vacuous behavior, since the pattern cannot fail to match. The compiler should probably emit an error or at least a warning when this happens.
>>>>>> 
>>>>>> The second is our other weird use of the `case` keyword, `for case`, which is now an orphan in the language. I see several ways this could be handled:
>>>>>> 
>>>>>> 1. Drop the `for case` functionality entirely; if you want that behavior, use a pattern-matching `if`.
>>>>>> 2. Replace the loop variable slot in the `for` statement with a pattern. This would force you to put `let` on all simple `for` statements.
>>>>>> 3. Try to automatically distinguish between simple variables/tuples and patterns in this slot. What could possibly go wrong?
>>>>>> 4. Require an equals sign before the `in`, like `for let foo? = in optionalFoos`. Looks a little gross, but it's unambiguous.
>>>>>> 5. Replace `for case` with `for if`, like `for if let foo? in optionalFoos`. This helps flag the unusual conditional behavior of this form of `for`.
>>>>>> 6. Just keep `for case` and don't worry about the fact that it's not parallel to the other statements anymore.
>>>>>> 
>>>>>> Thoughts on any of this?
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> * `if case` also has the problem that the `=` isn't appropriate unless you happen to bind some of the data matched by the pattern, but I don't know how to address that. A prior version of this proposal suggested saying `:=` instead of `=`, with the idea that `:=` could become a general pattern-matching operator, but the people I talked over this post with hated that.
>>>>>> 
>>>>>> -- 
>>>>>> Brent Royal-Gordon
>>>>>> Architechies
>>>>>> 
>>>>>> _______________________________________________
>>>>>> swift-evolution mailing list
>>>>>> 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
>>>> 
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> 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
>>> 
>> 
> 
> _______________________________________________
> 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/20160203/fca1b717/attachment-0001.html>


More information about the swift-evolution mailing list