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 <[email protected]> wrote:
> On Aug 18, 2016, at 8:19 AM, Xiaodi Wu via swift-evolution < > [email protected]> 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 < > [email protected]> wrote: > >> >> On Aug 18, 2016, at 3:42 PM, Slava Pestov <[email protected]> wrote: >> >> >> On Aug 18, 2016, at 12:52 AM, David Hart via swift-evolution < >> [email protected]> wrote: >> >> Opinions inline: >> >> On 18 Aug 2016, at 07:43, Sikhapol Saijit via swift-evolution < >> [email protected]> 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 >> [email protected] >> 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 >> >> > _______________________________________________ > 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
