> On Sep 24, 2016, at 7:31 PM, Dany St-Amant <[email protected]> wrote:
>
> Reading some old threads...
>
> Le 16 août 2016 à 15:12, Xiaodi Wu via swift-evolution
> <[email protected] <mailto:[email protected]>> a écrit :
>
>> On Tue, Aug 16, 2016 at 12:14 PM, Justin Jia <[email protected]
>> <mailto:[email protected]>> wrote:
>> I was trying to argue with reason. You kept stating your *opinion*. You
>> still didn't explain *why*. For example, why "if statements can and function
>> calls can't" and why "this severely violates user expectations"?
>>
>> These were not meant as expressions of opinion. Currently, in Swift, `if`
>> statements can short circuit and function calls cannot. Your proposal would
>> introduce short-circuiting where currently there can be none, and thus it
>> would severely violate user expectations.
>
> Function calls can currently be short-circuited... if there's try and throw
> involved.
>
> let z = try g(f1(a), f2(b), f3(c)) // Must be within do {} catch {}
>
> Assuming f1(), f2(), f3() can all throws, f2() is only called if f1() did not
> throw, and f3() if neither f1() nor f2() threw. The behavior is/seems to be:
> Pure left to right execution order regardless of throwing ability.
>
> If g() doesn't throw, the above (with exact same behavior) can be written
> more verbosely and explicitly as:
>
> let y = g(try f1(a), try f2(b), try f3(c)) // Must be within do {} catch {}
>
> Yet another way to do the call is:
>
> let x = try? g(f1(a), f2(b), f3(c)) // z is nil on throws
>
> So implementing something like what Justin is asking should fit within Swift,
> as long as it follows the try short-circuit logic. However, the use of a
> trailing question-mark to provide this functionality is not explicit enough
> to my taste (and to my weakening eyesight). Could we reuse let but with a
> question-mark? Or maybe guard.
>
Good point! Edge cases always exist.
> let z = g(let? f1(a), let? f2(b), let? f3(c))
> // z is nil on fX() == nil, otherwise Optional(g()) just like try? wrapping
> // left to right short-circuit à la let z = try?
>
This sounds like a good idea. Moving the keyword to the front is more explicit.
But `let? f1(a)` seems a little bit weird to me.
What about `let x = if? foo(x, y)` But… should we introduce `if!` as well?
```
func foo(x: Any, y: Any) { … }
let x: Any? = nil
let y: Any? = nil
if? foo(x, y) // If x and y both can be unwrapped, execute foo().
if? foo(x?, y?) // Or if we want to be a little bit more clear, specify which
argument could be optional
```
```
if! foo(x, y) // Will crash if nil is found.
if! foo(x!, y!) // To be more clear
```
Backward compatibility:
```
foo(x!, y!) // warning: add if! before the function
```
> Though, this may ask for also supporting something like:
>
> let x = g(let? try? f1(a), let? try? f2(b), let? try? f3(c))
>
> This allow selective discard of throws, instead of a discard all of a plain
> 'let x = try? ...'
>
Yes.
> Dany
>
>> ... snip ...
>>>>
>>>>>> On Aug 16, 2016, at 1:16 AM, Xiaodi Wu <[email protected]
>>>>>> <mailto:[email protected]>> wrote:
>>>>>>
>>>>>> On Mon, Aug 15, 2016 at 12:07 PM, Xiaodi Wu <[email protected]
>>>>>> <mailto:[email protected]>> wrote:
>>>>>> On Mon, Aug 15, 2016 at 11:43 AM, Justin Jia
>>>>>> <[email protected] <mailto:[email protected]>>
>>>>>> wrote:
>>>>>> I believe the core team has considered 99% of the ideas in the mailing
>>>>>> list in the past, but it doesn’t mean we can’t discuss it, right?
>>>>>> .. snip ...
>>>>>>
>>>>>> Back to the original topic.
>>>>>>
>>>>>> I spent some time thinking and changed my mind again. I think solution 1
>>>>>> is most reasonable. It is consistent with if statements. Instead of
>>>>>> treating it as sugar for `if let`, we can treat it as sugar for `guard`,
>>>>>> which is much easy to understand and remember.
>>>>>>
>>>>>> -
>>>>>>
>>>>>> Below is the reason why I think this feature is important (quoted from
>>>>>> another email).
>>>>>>
>>>>>> The problem with `if let` is you need to call the function inside { }.
>>>>>>
>>>>>> ```
>>>>>> /* code 1 */
>>>>>> if let x = x, let y = y {
>>>>>> /* code 2, depends on x and y to be non-optional */
>>>>>> let z = foo(x, y)
>>>>>> if let z = z {
>>>>>> bar(z)
>>>>>> }
>>>>>> /* code 3, depends on x and y to be non-optional */
>>>>>> }
>>>>>> /* code 4 */
>>>>>> ```
>>>>>>
>>>>>> I can't use `guard` for this situation because guard will force me to
>>>>>> leave the entire function.
>>>>>>
>>>>>> ```
>>>>>> /* code 1 */
>>>>>> guard let x = x, y = y else { return }
>>>>>> /* code 2, depends on x and y to be non-optional */
>>>>>> guard let z = foo(x, y) else { return }
>>>>>> bar(z)
>>>>>> /* code 3, depends on x and y to be non-optional */ <- This won't
>>>>>> execute if z is nil
>>>>>> /* code 4 */ <- This won't execute if x, y or z is nil
>>>>>> ```
>>>>>>
>>>>>> Then surround it with a do block.
>>>>>>
>>>>>> ```
>>>>>> out: do {
>>>>>> guard foo else { break out }
>>>>>> guard bar else { break out }
>>>>>> /* other code */
>>>>>> }
>>>>>> ```
>>>>>>
>>>>>> Or, more idiomatically, since your use case is that you want /* code 4
>>>>>> */ to be executed no matter what, while everything else depends on x and
>>>>>> y not being nil:
>>>>>>
>>>>>> ```
>>>>>> defer { /* code 4 */ }
>>>>>> guard let x = x, let y = y else { return }
>>>>>> /* code 2 */
>>>>>> /* code 3 */
>>>>>> ```
>>>>>>
>>>>>>
>>>>>> What I really want is some like this:
>>>>>>
>>>>>> ```
>>>>>> / * code 1 */
>>>>>> let z = foo(x?, y?)
>>>>>> /* code 2, depends on x and y to be non-optional, use x? and y? */
>>>>>> bar(z?)
>>>>>> /* code 3, depends on x and y to be non-optional, use x? and y? */
>>>>>> /* code 4 */
>>>>>> ```
>>>>>> This is much easier to read. Sometimes people choose to use `guard` to
>>>>>> avoid `{ }`, which usually lead to code could easily get wrong (like the
>>>>>> second example).
>>>>>>
>>>>>> Sincerely,
>>>>>> Justin
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>> On Aug 15, 2016, at 11:41 PM, Xiaodi Wu <[email protected]
>>>>>>> <mailto:[email protected]>> wrote:
>>>>>>>
>>>>>>> What do you mean, limited to variables? What about a computed property?
>>>>>>> You will have the same problem.
>>>>>>>
>>>>>>> I'm not sure where you want to go with this, given that the core team
>>>>>>> has considered the same idea in the past and found these issues to have
>>>>>>> no good solution.
>>>>>>>
>>>>>>> On Mon, Aug 15, 2016 at 04:56 Justin Jia
>>>>>>> <[email protected]
>>>>>>> <mailto:[email protected]>> wrote:
>>>>>>> IMO I don't this bar should be evaluated unless we decide if let can
>>>>>>> accept non-optional values.
>>>>>>>
>>>>>>> Actually, what if we allow if let to accept non-optional values?
>>>>>>>
>>>>>>> I agree this is confusing at the beginning. But people who are not
>>>>>>> familiar with the detail design can avoid this situation easily. People
>>>>>>> who are familiar with the design can adopt it quickly. Sometimes, this
>>>>>>> is unavoidable.
>>>>>>>
>>>>>>> Btw, do you think this is still something nice to have if we limit this
>>>>>>> syntax to only variables?
>>>>>>>
>>>>>>> On Aug 15, 2016, at 4:59 PM, Xiaodi Wu <[email protected]
>>>>>>> <mailto:[email protected]>> wrote:
>>>>>>>
>>>>>>>> On Mon, Aug 15, 2016 at 3:55 AM, Xiaodi Wu <[email protected]
>>>>>>>> <mailto:[email protected]>> wrote:
>>>>>>>> On Mon, Aug 15, 2016 at 3:25 AM, Justin Jia via swift-evolution
>>>>>>>> <[email protected] <mailto:[email protected]>> wrote:
>>>>>>>>
>>>>>>>>> On Aug 15, 2016, at 4:09 PM, Charlie Monroe
>>>>>>>>> <[email protected] <mailto:[email protected]>> wrote:
>>>>>>>>>
>>>>>>>>> The example above was to better demonstrate the problem with *when*
>>>>>>>>> to evaluate the latter argument. Why should both arguments be
>>>>>>>>> evaluated *before* the if statement? If both calls return Optionals,
>>>>>>>>>
>>>>>>>>> if let x = bar(42), y = baz(42) { ... }
>>>>>>>>>
>>>>>>>>> is how would I write it without the suggested syntax - baz(42) will
>>>>>>>>> *not* be evaluated if bar(42) returns nil. Which bears a question why
>>>>>>>>> would
>>>>>>>>>
>>>>>>>>> foo(bar(42)?, baz(42)?)
>>>>>>>>>
>>>>>>>>> evaluate both arguments even if the first one is nil, making it
>>>>>>>>> incosistent with the rest of the language?
>>>>>>>>
>>>>>>>> I see your point. I understand that maybe 1/2 of the people think we
>>>>>>>> should evaluate both arguments and 1/2 of the people think we should
>>>>>>>> only evaluate the first argument.
>>>>>>>>
>>>>>>>> I changed my idea a little bit. Now I think you are right. We should
>>>>>>>> only evaluate the first argument in your example. It’s not only
>>>>>>>> because of inconsistent, but also because the language should at least
>>>>>>>> provide a way to “short-circuit” to rest of the arguments.
>>>>>>>>
>>>>>>>> If they want to opt-out this behavior, they can always write:
>>>>>>>>
>>>>>>>> ```
>>>>>>>> let x = bar(42)
>>>>>>>> let y = baz(42)
>>>>>>>> foo(x?, y?)
>>>>>>>> ```
>>>>>>>>
>>>>>>>> Well, that was just the easy part. Now, suppose bar is the function
>>>>>>>> that isn't optional.
>>>>>>>>
>>>>>>>> ```
>>>>>>>> foo(bar(42), baz(42)?)
>>>>>>>> ```
>>>>>>>>
>>>>>>>> Is bar evaluated if baz returns nil? If you want this syntax to be
>>>>>>>> sugar for if let, then the answer is yes.
>>>>>>>>
>>>>>>>> s/yes/no/
>>>>>>>>
>>>>>>>> If short-circuiting works left-to-right, then the answer is no.
>>>>>>>>
>>>>>>>> s/no/yes/
>>>>>>>>
>>>>>>>> (See? Confusing.)
>>>>>>>>
>>>>>>>> This is very confusing, and there is no good intuitive answer.
>>>>>>>>
>>>>>>>>
>>>>>>>> _______________________________________________
>>>>>>>> swift-evolution mailing list
>>>>>>>> [email protected] <mailto:[email protected]>
>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>
>>>>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution