On Thu, Aug 18, 2016 at 1:20 PM, John McCall <[email protected]> wrote:
> On Aug 18, 2016, at 10:11 AM, Xiaodi Wu <[email protected]> wrote: > On Thu, Aug 18, 2016 at 11:30 AM, John McCall <[email protected]> wrote: > >> On Aug 18, 2016, at 8:46 AM, Xiaodi Wu <[email protected]> wrote: >> The issue would be that, in the case of "try? foo()", nil and .some(nil) >> might mean very different things. >> >> >> This is true of a?.foo() as well. But yes, I think it is more likely >> that someone would want to treat them differently for try?. >> > > Agreed. > > My proposed solution was half-baked, but it may be workable--I'm not > suggesting typing decisions based on a dynamic property, of course. It'd be > something like this: > > `as?` would produce a result of a type named something like > CastingOptional<T>, which on assignment or essentially any other operation > is promoted/bridged/[insert much more correct term here] to an Optional<T> > like how T is automatically promoted to Optional<T>. However, `try?` will > not wrap a CastingOptional<T> into an Optional<Optional<T>>. > > > The way this is done for ?-chaining is that the result of the chain is > coerced to T?, for a fresh unbound type T. If the result is already of > type U?, T will be bound to U and there's no "stacking" of optionals; if > the result is a non-optional type V, T will be bound to V and therefore the > chain gains a level of optionality. I think that is simpler and more > consistent than inventing a new flavor of Optional with complex conversion > and defaulting rules. > Yeah, that's definitely much simpler. I'm just not sure I'd be comfortable with `try?` refusing to stack optionals for all arbitrary functions `(...) -> Optional<T>` just like it works for ?-chaining, for the reasons we just discussed. So, if we're to stick to consistent rules, I'd rather that `try?` continue to stack optionals all the time. The status quo isn't unteachable: if you have try? and as?, that's two question marks, so you get back two stacked optionals. That's livable. > John. > > > >> John. >> >> 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
