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

Xiaodi Wu xiaodi.wu at gmail.com
Mon Aug 15 12:16:36 CDT 2016


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?
>>
>
> No, it certainly doesn't! I'm saying that you haven't come up with a
> solution to a known problem with the idea.
>
>
>>
>> Assuming we have the following declaration:
>>
>> ```
>> func foo(a: Int, b: Int?, c: Int, d: Int?) -> Int
>> ```
>>
>> For this:
>>
>> ```
>> let z = foo(a: f1(), b: f2()?, c: f3(), d: f4()?) // z becomes optional
>> ```
>>
>> We have a few different “possible solutions”:
>>
>> 1. Short-circuiting from left to right. This is equivalent to:
>>
>> ```
>> var z: Int? = nil
>> let a = f1()
>> guard let b = f2() else { return }
>> let c = f3()
>> guard let d = f4() else { return }
>> z = foo(a: a, b: b, c: c, d: d)
>> ```
>>
>> 2. Short-circuiting from left to right for optionals. Then evaluate
>> non-optional parameters. This is equivalent to:
>>
>> ```
>> var z: Int? = nil
>> guard let b = f2() else { return }
>> guard let d = f4() else { return }
>> let a = f1()
>> let c = f3()
>> z = foo(a: a, b: b, c: c, d: d)
>> ```
>>
>> 3. Do not short-circuiting.
>>
>> ```
>> var z: Int? = nil
>> let a = f1()
>> let optionalB = f2()
>> let c = f3()
>> let optionalD = f4()
>> guard let b = optionalB else { return }
>> guard let d = optionalD else { return }
>> z = foo(a: a, b: b, c: c, d: d)
>> ```
>>
>> Like I said before, I agree that there is no intuitive solution to this
>> problem. However, I'm still not convinced that this feature is *not
>> important*.
>>
>> Thank you for pointing out the problem to me. I didn't notice it at the
>> time I wrote my first email. I really appreciate that. However, instead of
>> saying I don't know which is the best solution so let's assume the core
>> team made the right decision, we should discuss whether 1, 2, 3 is the best
>> solution. Or you can convince me we don't *need* this feature.
>>
>
> I'm going to convince you that 1, 2, and 3 are all bad solutions. Thus,
> this feature won't fly.
> The fundamental issue is that having this sugar means that I can no longer
> reason about the order in which code is executed. An innocuous statement
> such as `print(a(), b(), c(), d())`, once you mix in your proposed `?`
> syntax with some but not all of these function calls, might have d()
> executed before a(), after a(), or not at all. This is greatly damaging to
> the goal of writing clear, understandable code.
>
>
>>
>> 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
>>>>>
>>>>>
>>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160815/c6d8a92e/attachment.html>


More information about the swift-evolution mailing list