[swift-evolution] try? shouldn't work on non-method-call

Xiaodi Wu xiaodi.wu at gmail.com
Thu Aug 18 10:46:14 CDT 2016


The issue would be that, in the case of "try? foo()", nil and .some(nil)
might mean very different things.
On Thu, Aug 18, 2016 at 10:40 John McCall <rjmccall at apple.com> wrote:

> On Aug 18, 2016, at 8:19 AM, Xiaodi Wu via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> Lots of interesting points here. I do think there's an improvement
> possible here, but it's actually along the lines of Sam's original
> suggestion #3 (not vis-a-vis all of Swift, but specifically for how try?
> composes with as?):
>
> A. I'm in favor of the current behavior where try prefixes an entire
> statement: it solves the precise issue of multiple nested optionals or
> multiple unwrapping of optionals in the situation where one statement has
> calls to many throwing functions. It says instead, I want nil if anything
> in this statement throws, otherwise, give me .some(value).
>
> Sam--I think you may have misunderstood Charles's explanation. He's not
> saying "try?" attaches with lower or higher precedence as compared to
> "as?". Rather, I think the mental model is that "try?" prefixes the whole
> right-hand side (rhs), and if *any* call on the rhs throws, the whole rhs
> evaluates to nil, but if *any* call could potentially throw but doesn't,
> "try?" wraps the entire rhs and gives you .some(value). IMO, this is pretty
> sensible for the reason he gives.
>
> B. I'm in favor of warning instead of error, for precisely the internal
> discussion rationale communicated by Slava. I'm willing to live with "try?
> 42" being only a warning if that means my code won't stop compiling when
> someone decides a library function doesn't need to throw.
>
> Sam--here, changing warning to error would not solve your original
> problem, because in that example "try?" does prefix at least one throwing
> function, so you wouldn't get an error anyway.
>
> C. However, given the thinking in (A), I do think how "try?" composes with
> "as?" is a little counterintuitive or at least overly ceremonious, though
> technically it is possible to reason through.
>
> It's true that currently you can use the multiple nested optionals to
> figure out whether either a throwing function threw (but not which throwing
> function out of potentially more than one) or whether the cast did not
> succeed. But, since "try?" after all means "give me nil if anything
> throws," it kind of makes less sense that you get all this nesting and
> detailed information when it composes with "as?". If you really wanted that
> level of detail, you could always evaluate "try?" and "as?" in separate
> statements. What I'd propose instead is this:
>
> If "try?" is composed with "as?", and "as?" yields "nil", then "try?"
> should not wrap that value in another optional.
>
>
> We can't make the typing decision dependent on a dynamic property like
> whether the cast fails.  And I don't like the idea of changing its typing
> rule based on the form of the nested expression.  But we could make "try?
> foo()" avoid adding an extra level of optionality, the same way that
> "a?.foo()" does.
>
> John.
>
>
> Does that sound sensible?
>
>
> On Thu, Aug 18, 2016 at 3:54 AM, Sikhapol Saijit via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>>
>> On Aug 18, 2016, at 3:42 PM, Slava Pestov <spestov at apple.com> wrote:
>>
>>
>> On Aug 18, 2016, at 12:52 AM, David Hart via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>> Opinions inline:
>>
>> On 18 Aug 2016, at 07:43, Sikhapol Saijit via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>> Hi all,
>>
>>
>> Yesterday I tried this code:
>>
>> func couldFailButWillNot() throws -> Any {
>>     return 42
>> }
>>
>> if let a = try? couldFailButWillNot() as? Int {
>>     print(a)
>> }
>>
>> And was surprised that the output was *Optional(42)* on both Swift 2 and
>> Swift 3.
>> I always have the impression that when a variable is resolved with if let
>>  it will never be optional.
>>
>> So, with a little investigation, I found out that it happens because as? has
>> higher precedence than try? and is evaluated first.
>> And the whole expression `try? couldFailButWillNot() as? Int` evaluated
>> as *Optional(Optional(42))*.
>>
>> Also, I’m surprised that try? can be used with non-method-call.
>> This code: `print(try? 42)` will print *Optional(42)*.
>>
>> So, the questions are:
>>
>> 1. Is it intentional that try? can be used with a "non-method-call" and
>> return an optional of the type that follows?
>>
>>
>> I think this is the real solution. try and try? should not be allowed on
>> non-throwing functions or expressions.
>>
>>
>> This is a warning right now — do you think it should be an error?
>>
>> Slavas-MacBook-Pro:~ slava$ cat ttt.swift
>> func f() {}
>>
>> func g() {
>>   try f()
>>   try? f()
>> }
>>
>> Slavas-MacBook-Pro:~ slava$ swiftc ttt.swift
>> *ttt.swift:4:3: **warning: **no calls to throwing functions occur within
>> 'try' expression*
>>   try f()
>> *  ^*
>> *ttt.swift:5:8: **warning: **no calls to throwing functions occur within
>> 'try' expression*
>>   try? f()
>> *       ^*
>>
>>
>> Thank you Slava,
>>
>> While I think using try/try? on anything but a throwing function call
>> should be an error, right now it even works with anything. `try? 42` will
>> just wrap 42 in an optional and give some warning now.
>>
>>
>>
>> 2. Should we design try? to have higher precedence than as? or any
>> operators at all?
>> My intuition tells me that
>> let a = try? couldFailButWillNot() as? Int
>> should be equivalent to
>> let a = (try? couldFailButWillNot()) as? Int
>>
>>
>> That’s worth considering. try feels like it should tie very strongly with
>> the throwing expression.
>>
>> 3. Do you think that doubly-nested optional (or multi-level-nested
>> optional) is confusing and should be removed from Swift? (Yes, I’ve seen
>> this blog post Optionals Case Study: valuesForKeys
>> <https://developer.apple.com/swift/blog/?id=12>).
>> For me *Optional(nil)* (aka *Optional.Some(Optional.None))*) doesn’t
>> make much sense.
>> Maybe, one of the solution is to always have optional of optional merged
>> into a single level optional? Like *Optional(Optional(Optional(42)))* should
>> be the merged to and evaluated as *Optional(42)*.
>>
>>
>> I don’t think this is the solution. Even if it was, how would you expect
>> to “remove” them from Swift? Optionals are simply an enum with an
>> associated value. We’d have to introduce a language feature to restrict
>> values that can be stored in enum cases? It sounds awfully complicated.
>>
>> BTW, the code above is merely for a demonstration. The actual code was
>> more of something like this:
>>
>> func parse(JSON: Data) throws -> Any {
>>     // …
>> }
>>
>> if let dict = try? parse(JSON: json) as? [String: Any] {
>>     // assume dict is a valid [String: Any] dictionary
>>     // …
>> }
>>
>> I’m new to this mailing list so I’m not sure if this belongs here. I’m
>> sorry in advance if it doesn’t.
>>
>>
>> Thank you,
>> Sam
>> _______________________________________________
>> 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
>>
>>
> _______________________________________________
> 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/20160818/456d062b/attachment.html>


More information about the swift-evolution mailing list