I use map/flatMap when I have an optional parameter to a method:

let result = x.map { foo.bar(x: $0) }

My only grief is that it doesn't work too well when you have many optionals 
that you want to unwrap and use as parameters.

I tend to agree that short-circuiting call expressions from their arguments 
could be confusing, especially for functions that take a lot of parameters (5+, 
which is not that uncommon in Cocoa), and even more so when you have many 
short-circuiting parameters. I can see that it wouldn't be too obvious why a 
function isn't called. With || and &&, the reason that the next condition is 
called or not is extremely clear.

You could argue that a short-circuiting && wouldn't make consensus (even though 
I think it would), but replacing a short-circuiting || results in much uglier 
code than the code that you're trying to get around with this new 
short-circuiting argument construct.

Félix

> Le 16 août 2016 à 08:31:50, Xiaodi Wu via swift-evolution 
> <[email protected]> a écrit :
> 
> We don't design the language in a vacuum. If statements can short-circuit and 
> function calls can't. You are proposing a function call that can 
> short-circuit. This severely violates user expectations.
> On Tue, Aug 16, 2016 at 10:03 Justin Jia <[email protected] 
> <mailto:[email protected]>> wrote:
> I will reply both of your email in this simple email.
> 
> 
>> On Aug 16, 2016, at 10:26 PM, Xiaodi Wu <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>> Top-replying because Google is forcing me to:
>> 
>> If you want to print an error for all early exits, declare a variable to 
>> keep track of exit status such as `var isEarlyExit = false`, then use a 
>> defer block that prints `error` only after checking `isEarlyExit` (or, you 
>> know, design your code so that `error` itself would be nil if you're exiting 
>> without an error).
>> 
> 
> IMHO `var is EarlyExist = false` is really ugly. Sometimes we can’t design 
> our code so that `error` itself would be nil if existing without an error 
> simply because we de depending on Cocoa Touch and many third party 
> frameworks. 
> 
>> It is not "really bad" if your code "fails" unless the lines of code are 
>> executed in the explicitly written order. There are no tricks hidden in that 
>> behavior: lines of code are *supposed* to be executed from top to bottom in 
>> the absence of a control flow statement, because Swift is a procedural 
>> programming language. Proceeding from one line to the next is the absolute 
>> most primitive flow of control.
>> 
> 
> Not always, but sometimes. Maybe I should say it’s “better” if changing the 
> order of the code won’t produce any unintentional behaviors? We are talking 
> about how to improve Swift, right?
> 
>> `guard` and `defer` were introduced in a later version of Swift to solve a 
>> practical problem encountered in daily use, the nested pyramid of doom from 
>> too many `if let` blocks. The point is that `guard` and `defer` together 
>> constitute an ingenious and *complete* solution to that problem; you have 
>> not shown me any scenario that cannot be trivially refactored to avoid 
>> nested blocks using these two language constructs. So more sugar is not 
>> necessary to solve this problem.
>> 
> 
> Well, it’s Turing Complete. I can’t argue against it. But I can give you an 
> example that needs multiple defer. I think this greatly hinders readability. 
> Also, I think making our code less order independent is already important 
> enough.
> 
>> "This is not explicit enough" *is* an argument against almost any sugar you 
>> can propose. I think you are seeing why the core team is actively 
>> discouraging sugar proposals on this list. Unless something comes along that 
>> totally blows the alternative out of the water, I'm inclined to agree that 
>> more sugar is almost a non-goal for Swift.
>> 
>> (What would be something that could change my mind? Here would be my 
>> criteria:
>> 
>> * The non-sugared version is extremely painful to write (>>5 LOC, maybe 
>> >>20), difficult to write correctly, and even if correctly written, does not 
>> express the intended solution clearly to the reader.
>> 
> 
> I don’t know how many time you spent on writing swift code in the past. I 
> also don’t know whether your code depends on Cocoa or not. At least, 
> personally, this is the no.1 request in my wish list. I think `if let` is 
> extremely painful to write (not because >>20, but because it occurs too often 
> and keeps bugging me). Maybe you feel differently. Then it’s really hard for 
> me to convince you and it’s also really hard for you to convince me. Time 
> will tell how many developers want this feature.
> 
>> * There is a single, overwhelmingly obvious, universally or nearly 
>> universally appropriate solution, and the proposed sugar would always be a 
>> shorthand for that one solution.
>> 
> 
> If we choose to reinvent if statements, short-circuiting will not be a nearly 
> universally appreciate solution. Not even close. 
> 
>> Something like a copy-on-write attribute would fit the bill, because good 
>> luck implementing that by hand over and over again, and if you're a reader 
>> of code, good luck verifying that all that code does what you think.)
> 
> Maybe. But’s that’s another story.
> 
>> I have already explained why your proposal is not at all like optional 
>> chaining. Your proposal hides complicated control flow changes, but optional 
>> chaining does not.
>> 
>> It does not do you any good to argue that "most people won't nest functions 
>> inside functions". First of all, that's an unbelievable claim. Second of 
>> all, computed properties can have side effects, since they are essentially 
>> functions under the hood. Have you never referred to `foo.bar` inside a 
>> function call? You literally cannot know if a property is computed, 
>> potentially with side effects, unless you inspect the source code. Thus, a 
>> programmer cannot know if they "choose to nest functions inside functions". 
>> It does not matter if they are a genius.
> 
> 
> Same apply to if statement. IMO, this paragraph can be used to argue against 
> all statements that will short-circuit in some way. I’m still not convinced 
> why `if` can be used but the proposed solution can’t.
> 
>> On Mon, Aug 15, 2016 at 23:56 Justin Jia <[email protected] 
>> <mailto:[email protected]>> wrote:
>>> On Aug 16, 2016, at 1:51 AM, Xiaodi Wu <[email protected] 
>>> <mailto:[email protected]>> wrote:
>>> 
>>> On Mon, Aug 15, 2016 at 12:31 PM, Justin Jia 
>>> <[email protected] <mailto:[email protected]>> 
>>> 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 <[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?
>>>> 
>>>> 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 <[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]
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to