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

Dennis Lysenko dennis.s.lysenko at gmail.com
Sun Sep 25 03:36:43 CDT 2016


Oops, completely missed about 57 messages in the meat of this discussion,
so apologies if what I said was already suggested.

On Sun, Sep 25, 2016 at 4:35 AM Dennis Lysenko <dennis.s.lysenko at gmail.com>
wrote:

> Could take a page out of Kotlin's book:
>
> x.let { foo(it) } // default argument name that isn't $0
>
> IMO, this syntax looks better as a one liner than "if x? { foo(x) }".
>
> The edge case here, with foo and bar being optional-returning functions,
> becomes:
> (foo(42), bar(43)).let { x, y in foo(x, y) } // default argument "it" here
> is a tuple so you can't pass it directly to the function, therefore you
> have to name x and y explicitly
>
> In which case it should be abundantly clear to the reader that there is NO
> short-circuiting and both foo and bar are evaluated as a tuple before the
> "let" function is called on that tuple.
>
> On Sun, Sep 25, 2016 at 3:24 AM Justin Jia via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>> On Sep 24, 2016, at 7:31 PM, Dany St-Amant <dsa.mls at icloud.com> wrote:
>>
>> 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.
>>
>>
>> Good point! Edge cases always exist.
>>
>> 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?
>>
>>
>> This sounds like a good idea. Moving the keyword to the front is more
>> explicit. But `let? f1(a)` seems a little bit weird to me.
>>
>> What about `let x = if? foo(x, y)` But… should we introduce `if!` as well?
>>
>> ```
>> func foo(x: Any, y: Any) { … }
>>
>> let x: Any? = nil
>> let y: Any? = nil
>> if? foo(x, y) // If x and y both can be unwrapped, execute foo().
>> if? foo(x?, y?) // Or if we want to be a little bit more clear, specify
>> which argument could be optional
>> ```
>>
>> ```
>> if! foo(x, y) // Will crash if nil is found.
>> if! foo(x!, y!) // To be more clear
>> ```
>>
>> Backward compatibility:
>>
>> ```
>> foo(x!, y!) // warning: add if! before the function
>> ```
>>
>> 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? ...'
>>
>>
>> Yes.
>>
>> 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
>>
>> _______________________________________________
>> 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/20160925/19733da7/attachment.html>


More information about the swift-evolution mailing list