Make types Hashable

Currently Swift types arent Hashable. This is a problem e.g. in SwiftUI Environment, where theres a subscript<K: EnvironmentKey>(type: K.Type) but it cant be used directly @Environment(\.[MyKey.self]). The result is needless boilerplate (proxy property) in one of SwiftUI's core features.

I worked around this in https://github.com/pteasima/BetterEnvironment , but still its not as elegant as making the subscript available in a key path.

With how easy the solution seems to be (struct HashableType<Type>: Hashable { }), arguably it would be easy to add to Swift (the only thing stopping users from doing it is the fact that you cant extend metatypes). Im not a compiler expert and am ready to be educated. But stil, is this worth pursuing?

4 Likes

Formally, they're called Metatype.

1 Like

You could probably do something like this:

struct AnyMetatypeWrapper {
    let metatype: Any.Type
}

extension AnyMetatypeWrapper: Equatable {
    static func ==(lhs: Self, rhs: Self) -> Bool {
        lhs.metatype == rhs.metatype
    }
}

extension AnyMetatypeWrapper: Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.combine(ObjectIdentifier(metatype))
    }
}

extension Dictionary {
    subscript(_ key: Any.Type) -> Value? where Key == AnyMetatypeWrapper {
        get { self[AnyMetatypeWrapper(metatype: key)] }
        _modify { yield &self[AnyMetatypeWrapper(metatype: key)] }
    }
}

var dict: [AnyMetatypeWrapper: Any] = [:]
dict[String.self] = "String"
dict[Int.self] = "Int"
print(dict[String.self]!) // String
print(dict[Int.self]!) // Int

This is an example of wrapping metatypes and using ObjectIdentifier for hashing. This is quite useful when you want to use metatypes as keys for dictionary (which requires Hashable). Perhaps you can adapt this to what you want to do?

Nice, I think this is another solution (this time AnyMetatypeWrapper isnt generic over the type, unlike my solution).

But the problem is that your subscript still cant be used in a KeyPath, which is what I was going for all along and perhaps didnt make it clear enough with the @Environment example.

I am aware that you can make a version of the subscript that takes something hashable, instead of Any.Type. But thats what Im trying to avoid (and why Im on Swift evolution forums, since I believe it needs to be a Swift feature).

Terms of Service

Privacy Policy

Cookie Policy