Why must subscript parameter types be Hashable for key-paths?

In most of the cases the types I use in subscripts were already Hashable so I never bumped into this error, and when I bumped into it I could fix it as it was okay to make the types conform to Hashable.

  • However I would like to understand why this restriction exists?

I know that a key-path itself is Hashable which lets me assume that every intermediate path must be constructed from something that is also Hashable. From a different view angle however, a subscript is similar to a method and it's a future direction to allow constructing key-paths for methods as well.

  • Would this mean that any method parameter type would also be required to be Hashable?
    This seems like it could cause a bigger issue.

  • If the answer to previous question is 'no', then why are subscripts parameter types required to conform to Hashable today?

class Key {
  init() {}
}

struct S {
  subscript(key: Key) -> Int {
    return 42
  }
}

let s = S()
\S.[Key()].description // error: Subscript index of type 'Key' in a key path must be Hashable

cc @Joe_Groff

The restriction is mentioned in the original proposal, buy can you elaborate why this the case and what would it mean for key-paths for methods?

Forming a key path through subscripts (e.g. Array / Dictionary) will have the limitation that the parameter's type(s) must be Hashable . Should the archival and serialization proposal be accepted, we would also like to include Codable with an eye towards being able to make key paths Codable themselves in the future.

Method key path components would have the same restriction as subscript indexes. In order for KeyPath itself to be hashable, all the components it captures have to be as well.

3 Likes

Ugh, that's a bummer. Do you think it would have been possible to design key-paths the way they are now but with conditional hashability/equality depending on the full key-path? This of course is not directly expressible in pure Swift, but maybe with some compiler magic from your Swift wizard skills?! :slight_smile:

The current key path design is a compromise to provide a good subset of useful functionality within the confines of the language today. With more flexible existential types, it would make sense to turn KeyPath into a protocol, and allow Hashable to be composed with it when desired.

If you're concerned about method components in key path literals used as function arguments, SE-249, there's no fundamental reason that literals used as functions need to be constrained to the limitations of KeyPath.

2 Likes