[swift-evolution] ? suffix for <, >, <=, >= comparisons with optionals to prevent subtle bugs

Al Skipp al_skipp at fastmail.fm
Thu Dec 10 12:21:32 CST 2015


Interesting stuff!

It did confuse me too, as I was expecting your initial code sample to work. My understanding is that ‘?’ is syntactic sugar for ‘map’ in this context. Anything following the ‘?’ is inside the Optional context.

Here is a comparison between ‘map’ and optional chaining (with and without parentheses).

let type1 = peeps[0].pet.map { $0.age }.dynamicType // Optional<Int>.Type
let type2 = peeps[0].pet.map { $0.age.dynamicType } // Int.Type

(peeps[0].pet?.age).dynamicType // Optional<Int>.Type
peeps[0].pet?.age.dynamicType // Int.Type


> On 10 Dec 2015, at 18:01, Greg Titus <greg at omnigroup.com> wrote:
> 
>> 
>> On Dec 10, 2015, at 9:30 AM, thorsten--- via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>>>> let ps = peeps.filter { $0.pet?.age.map { age in age < 6 } ?? false }
>>> 
>> 
>>> It’s curious that your example won’t compile (didn’t when I tried it in a Playground)
>> 
>> You are right, the example does not compile (wrote it in the train far away from Xcode). It gives an error of „value of type ‚Int‘ has no member ‚map‘“.
>> 
>> Hmm, I consider that as a bug in the definition of optional chaining, as the documentation clearly states 
>> "Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil. If the optional contains a value, the property, method, or subscript call succeeds; if the optional is nil, the property, method, or subscript call returns nil.“ (note the last sentence).
>> 
>> So if $0.pet?.age should return nil in case of $0.pet being nil, the type of $0.pet?.age has to be Int? and therefore understand map().
>> 
>> peeps[0].pet?.age.dynamicType      // => Int.Type this is wrong!! Should be Optional<Int>.Type
>> let y: Int = peeps[1].pet?.age     // This gives a type error which is right (but inconsistent with the dynamicType just claimed)
>> let x = peeps[1].pet?.age          // Now let’s see what the type is after assigning the expression
>> x.dynamicType                      // => Optional<Int>.Type as expected
>> x.map { age in age < 6 } ?? false  // => false as expected
>> 
>> So, obviously a bug in the compiler.
>> 
> 
> This works: 
> 
> let ps = peeps.filter { ($0.pet?.age).map { age in age < 6 } ?? false }
> 
> Essentially the “?.” operator unwraps the optional, and any further “.” in the chain get applied with the non-optional types as long as the results are non-nil. In order to get an optional result again, you need to break the “.” chain, as with the parens in this case.
> 
> I’m not sure: is this actually a compiler bug, and the type constraint system ought to consider binding to members of “T?" as well as “T" at each further “.”, or is this an expected (but obscure) part of the language, in which case the bug is maybe just that if no members match for “T" the error could suggest this paren fix if there is a member match for “T?”?
> 
> 	- Greg

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151210/33a947cf/attachment.html>


More information about the swift-evolution mailing list