[swift-evolution] [Idea] Use optionals for non-optional parameters

Dany St-Amant dsa.mls at icloud.com
Sat Sep 24 21:31:22 CDT 2016


Reading some old threads...

> Le 16 août 2016 à 15:12, Xiaodi Wu via swift-evolution <swift-evolution at swift.org> a écrit :
> 
>> On Tue, Aug 16, 2016 at 12:14 PM, Justin Jia <justin.jia.developer at gmail.com> 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.

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?

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? ...'

Dany

>> ... snip ...
>>>>>> 
>>>>>>>>> On Aug 16, 2016, at 1:16 AM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>>>>>>> 
>>>>>>>>>> On Mon, Aug 15, 2016 at 12:07 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>>>>>>>>> On Mon, Aug 15, 2016 at 11:43 AM, Justin Jia <justin.jia.developer at gmail.com> 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 <xiaodi.wu at gmail.com> 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 <justin.jia.developer at gmail.com> 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 <xiaodi.wu at gmail.com> wrote:
>>>>>>>>>>>>> 
>>>>>>>>>>>>>>> On Mon, Aug 15, 2016 at 3:55 AM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>>>>>>>>>>>>>> On Mon, Aug 15, 2016 at 3:25 AM, Justin Jia via swift-evolution <swift-evolution at swift.org> wrote:
>>>>>>>>>>>>>>> 
>>>>>>>>>>>>>>>> 
>>>>>>>>>>>>>>>>> On Aug 15, 2016, at 4:09 PM, Charlie Monroe <charlie at charliemonroe.net> 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
>>>>>>>>>>>>>>>> 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/20160924/0d3a3fbd/attachment.html>


More information about the swift-evolution mailing list