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

Xiaodi Wu xiaodi.wu at gmail.com
Mon Aug 15 12:51:39 CDT 2016


On Mon, Aug 15, 2016 at 12:31 PM, Justin Jia <justin.jia.developer at gmail.com
> wrote:

>
> Since you mentioned do and defer:
>
> ```
> func foo(wantsToBreak: Bool) {
>     out: do {
>         defer { print("Hello, world!") }
>         guard wantsToBreak else { break out }
>     }
>     print("End of function.")
> }
>
> foo(wantsToBreak: true) // Output: Hello, world!\nEnd of function.
> foo(wantsToBreak: false) // Output: Hello, world!\nEnd of function.
> ```
>
> Do you think this is confusing?
>

No, I don't. But I also don't see why you would put `defer` inside `do`
like that. `defer` and `guard` can be used profitably without nesting
inside blocks.


> At least it confused me in the fast. However, defer is still very useful.
>
> Even if I choose to use guard, defer and do, it will still look like the
> one with `if let`. Lots of blocks. The code should be straightforward
> without any brackets.
>

Huh? I don't buy this argument at all. You don't like the look of `{ }`, so
you are proposing new sugar using `?`--is that what you're claiming? This
sounds to me like the same motivation as that behind early suggestions to
move to a Python-like syntax.


> See this example (since it’s a lot of code I rendered a PDF).
>

I don't see the motivation in this example. Why wouldn't you just move the
code to update `cell.heading` right after you guard that `imageName` is not
nil?


> 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@
>> 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/0f18dce0/attachment.html>


More information about the swift-evolution mailing list