Static reflectable

I like this strong type system in Swift. It saves lots of runtime errors. I wonder if we could get more from it. For instance, adding a property to a type can be error-prone.

struct Filters {

    let keywords: Set<String>?
    let tags: Set<Tag>?
    
    var count: Int {
        var c = 0
        if let keywords = keywords, !keywords.isEmpty {
            c += 1
        }
        if let tags = tags, !tags.isEmpty {
            c += 1
        }
        return c
    }

    func reset() { ... }
}

Adding a new filter property to Filters is prone to introduce an error in count, reset(), etc... An exhaustive switch over all properties in places like these methods would cause static errors that could help to avoid logic errors.

var count: Int {
    var c = 0
    for child in StaticMirror(reflecting: Self.self).children {
        switch child {
            case .keywords:
                if let keywords = keywords, !keywords.isEmpty {
                    c += 1
                }
            case .tags:
                if let tags = tags, !tags.isEmpty {
                    c += 1
                }
        }    
    }   
    return c
}

Now, I see that this looks weird and has this time complexity. An API or change in the language (better syntax?) is to be discussed. I'm just asking if such feature could be appreciated.

1 Like

To my knowledge, Swift's reflection system is due for a revamp, but it's lower priority than the work being done now, so I guess it's waiting for its time to shine.

Meanwhile, you can use my library that provides a convenient wrapper around Swift's hidden and more powerful reflection system that ought to do what you want.

I don't see how introspection-kit could do what I want. I suppose this cannot be done without some work on compiler side.

I wonder if something like the compiler does for Encodable and Decodable, synthetizing CodingKey if wasn't defined explicitly, could be available to client?

You can use it to iterate through stored properties of your type without having an instance to it. For each of those properties, you have extensive information, like name, type and ways of accessing them given an instance of that type. You can rewrite your example to use for propertyIntrospection in TypeIntrospection(rawValue: Self.self).properties and get all the info you need

I keep writing code resembling this shape & I also implement it with a switch inside a for loop. The loop seems a bit wasteful, and even more syntactically noisy, but it's the best we have at the moment. (My gut says LLVM takes the opportunity to unroll the for loop when it sees code like this, but I really don't know.)

If we had a little extra bit of support from the language, it would be nice to drop the loop in favor of something more direct, like a switch all statement over any CaseIterable type. Its benefits would be that it could be reasoned about much more like straight-line code, while also checking for exhaustiveness.

And instead of the Mirror type: if we had a way to acquire an enumeration of KeyPaths from the type, we could write this:

var count: Int {
    var c = 0
    
    switch all Self.Properties {
    case \.keywords:
        c += (keywords?.isEmpty == false) ? 1 : 0
    
    case \.tags:
        c += (tags?.isEmpty == false) ? 1 : 0
    }
    
    return c
}

(If _.Properties turned out to break a lot of code, maybe Properties(of: Self.self) instead.)

1 Like

I agree that KeyPaths is more powerful than plain property representing cases. For instance, implicit CodingKey could become just an alias for this more abstract representation. I can imagine lots of utilizations for all keypath cases available.

Of course, this cannot be synthetized implicitly for every type. I suppose a protocol would do, like Equatable, Hashable, etc do. I'm not sure about domain though. Whether more abstract e.g. StaticReflectable that could provide more APIs in the future or more narrow e.g. KeyPathIterable.

Once we have this, the switch all would be a great support!

There was a pitch for a StoredPropertyIterable protocol in 2019. I think this came out of the Swift for TensorFlow project.

If you search the forum for "StoredPropertyIterable", you'll probably find more discussion about it.

2 Likes

Thanks @ole! For a moment I was like confused why there is no demand for this... So I'll wait until reflection gets revised. Noting this scenario to be exercised when an official pitch will come up.