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

Xiaodi Wu xiaodi.wu at gmail.com
Thu Aug 18 10:19:48 CDT 2016


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.

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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160818/31b58a93/attachment.html>


More information about the swift-evolution mailing list