I see a pattern of myself writing code like this now:

```
func foo(x: Int?) -> Int? {
    guard x = x else { return nil }
    /* normal function */
}
```

This is even worse than the x? syntax. But sometimes I have no other choice. 
Most of my functions are without side effects and… I don’t want to write if let 
x = x every time I call this function.

> On Aug 16, 2016, at 12:56 PM, Justin Jia <justin.jia.develo...@gmail.com> 
> wrote:
> 
>> 
>> On Aug 16, 2016, at 1:51 AM, Xiaodi Wu <xiaodi...@gmail.com 
>> <mailto:xiaodi...@gmail.com>> wrote:
>> 
>> On Mon, Aug 15, 2016 at 12:31 PM, Justin Jia <justin.jia.develo...@gmail.com 
>> <mailto:justin.jia.develo...@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.
>>  
> 
> Because I don’t want `defer` to execute outside do block. Let me give you a 
> simplified example: I wanted to print error for all early exits except normal 
> return (reaches last line). I would like to use defer otherwise I need to 
> write `else { print(error); return }` for all guards. The intuitive way of 
> achieving this for me was to nest defer inside do blocks. But it turned out 
> that defer will be executed even if you choose to break a block. I’m not 
> arguing this is a bad design decision. My point is: sometimes non-intuitive 
> design decisions are non-avoidable.
> 
> 
>> 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?
>>  
> 
> I already explained why. It was just a naive example. In real life methods 
> can be a lot more complicated than my example. It’s really bad if your code 
> will fail unless it follows the same exact order. We need to modify our code 
> everyday, and most of the time we are working on code that is not even 
> written by ourselves. If you scan through methods with name like updateCell, 
> intuitively, you will think the order of the code will not matter. And it 
> shouldn’t! It is really easy to make mistakes with guard statement because 
> the order matters here. IMO, guard is only useful if we place it at the 
> beginning of the function—for all or nothing.
> 
> Why we chose to use brackets and indentation? Because they can warn us that 
> the behavior of the code will change. Either the outcome will vary (if) or 
> the code will be executed for more than one time (for). Checking an object if 
> is nil doesn’t always belong here. Using `if let` is not being explicit. It’s 
> boilerplate. A not-so-good fix for the side effect of optionals. Most of the 
> time, we want the flow to be “flat”. That’s why swift supports `guard` and 
> `object?.method`. If you think `foo(x?)` is not important, do you think 
> `guard` and `object?.method` are also not important?
> 
> I understand that Swift is designed to be explicit. I also agree with it. But 
> I saw an unhappy trend in the mailing list: "this is not explicit enough" can 
> be used to argue against anything. Shall we remove @autoclosure? Shall we 
> remove trailing closures? Shall we remove `object?.method`?
> 
>>> On Aug 16, 2016, at 1:16 AM, Xiaodi Wu <xiaodi...@gmail.com 
>>> <mailto:xiaodi...@gmail.com>> wrote:
>>> 
>>> On Mon, Aug 15, 2016 at 12:07 PM, Xiaodi Wu <xiaodi...@gmail.com 
>>> <mailto:xiaodi...@gmail.com>> wrote:
>>> On Mon, Aug 15, 2016 at 11:43 AM, Justin Jia 
>>> <justin.jia.develo...@gmail.com <mailto:justin.jia.develo...@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...@gmail.com 
>>>> <mailto:xiaodi...@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.develo...@gmail.com 
>>>> <mailto:justin.jia.develo...@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...@gmail.com 
>>>> <mailto:xiaodi...@gmail.com>> wrote:
>>>> 
>>>>> On Mon, Aug 15, 2016 at 3:55 AM, Xiaodi Wu <xiaodi...@gmail.com 
>>>>> <mailto:xiaodi...@gmail.com>> wrote:
>>>>> On Mon, Aug 15, 2016 at 3:25 AM, Justin Jia via swift-evolution 
>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>>> 
>>>>>> On Aug 15, 2016, at 4:09 PM, Charlie Monroe <char...@charliemonroe.net 
>>>>>> <mailto:char...@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@swift.org <mailto:swift-evolution@swift.org>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to