Why does compactMap { $0.property } work but compactMap(\.property) doesn't with non-optional properties?

Makes sense!
Thanks.

1 Like

(T) -> U is not a subtype of (T) -> U?

2 Likes

It is a subtype, that's why you're allowed to do this:

protocol P {
    init?()
}

struct T: P { }

print(T())

The problem is that the language doesn't have any generalized subtyping rules (except for subclasses), so anything the compiler does that uses this subtyping is special cased.

We've also got subtyping for protocols, Any, AnyObject, and possibly other stuff, not just classes. It works pretty much the same for all of them.

The relationship between functions behaves differently, so I wouldn't use the same word for it. As you say - there's a bunch of special cases around functions, but they're not subtypes in the same way as classes.

Function types do have subtyping: they're contravariant in (non-inout) parameter types and covariant in result types. However, the conversion is not dynamically reversible using is, as?, or as!.

4 Likes

in that case documentation at https://github.com/apple/swift/blob/main/docs/DynamicCasting.md should be fixed (the only place I could find information on what is a subtype in swift)

  • T is a subtype of a non-protocol type U iff T.self is U.Type

I feel like this sub-typing talk might be getting off-track from the original question.

The relevant problem is all just about key path conversion. It helps to see the following in action for the original example:

The results of that, are that this compiles…

persons.compactMap(\.name as (Person) -> String)

…and so does this. :joy_cat:

persons.compactMap(\.name as (Person) -> String as (Person) -> String?)

But this doesn't (which I find remarkable, given the previous example).

persons.compactMap(\.name as (Person) -> String?)

Nor this, when it's not an argument, even without trying to optional-ize it:

\Person.name as (Person) -> String

And if you try to use explicit typing in any of those three compiling examples, then compilation will fail.

// Cannot convert value of type 'KeyPath<Person, String>' to type '(Person) -> String' in coercion
persons.compactMap(\Person.name as (Person) -> String)

Doesn't seem like there's any sense to it, to me. Just seems like an unfinished feature. :person_shrugging:

8 Likes

I suppose this bug report by @nicklockwood relates to this issue:
https://bugs.swift.org/browse/SR-13347

Did you mean this one? Issues · apple/swift · GitHub

1 Like

Apologies, I did indeed mean [SR-13347] compactMap has different semantics with closure than keyPath. · Issue #55787 · apple/swift · GitHub and I'm equally surprised at how I copied a wrong link and why your link just now doesn't seem to actually send me to SR-13347 but to a list of issues, with SR-15282 being shown. :man_shrugging:

Spun off a discussion in #evolution:discuss to raise the possibility of allowing the keypath construction here.

1 Like