I've discovered an ability of Swift but I'm not sure it's safe.
After modifying this StackOverflow answer, I've got a protocol that will give you, for any conforming object, a dictionary of [member variable name : its keyPath]. This functionality seems pretty magical to me:
protocol KeyPathListable {
associatedtype AnyOldObject
// require empty init as the implementation use the mirroring API, which require
// to be used on an instance. So we need to be able to create a new instance of the
// type. See @@@^^^@@@
init()
var keyPathReadableFormat: [String: Any] { get }
var allKeyPaths: [String:KeyPath<AnyOldObject, Any?>] { get }
}
extension KeyPathListable {
var keyPathReadableFormat: [String: Any] {
var description: [String: Any] = [:]
let mirror = Mirror(reflecting: self)
for case let (label?, value) in mirror.children {
description[label] = value
}
return description
}
var allKeyPaths: [String:KeyPath<Self, Any?>] {
var membersTokeyPaths: [String:KeyPath<Self, Any?>] = [:]
let instance = Self() // @@@^^^@@@
for (key, _) in instance.keyPathReadableFormat {
membersTokeyPaths[key] = \Self.keyPathReadableFormat[key]
}
return membersTokeyPaths
}
}
So if Tree conforms (there's some weird init() business that has to happen):
struct Tree: KeyPathListable {
var diameter: Double
var circumference: Double
var barkThickness: Double
}
extension Tree{
// Custom init inside an extension to keep auto generated `init(x:, y:)`
init() { //not called as of now
self.diameter = 10
self.circumference = 31.415
self.barkThickness = 0.5
}
}
Then I can call tree.allKeyPaths on an instance and get the magical dictionary I referred to:
var tree = Tree(diameter: 10, circumference: 31.415, barkThickness: 0.5)
for entry in tree.allKeyPaths{
print("member: ", entry.key, " / value: ", tree[keyPath: entry.value]!)
}
//prints
//
//member: barkThickness / value: 0.5
//member: circumference / value: 31.415
// member: diameter / value: 10.0
This functionality seems super cool to our team and we want to use it in some fundamental parts of our code.
One application is navigating a graph of objects and returning KeyPaths to interesting member variables. We will then use these KeyPaths to probe the member variables while training a machine learning model that uses the graph to produce predictions.
However, the original answerer on the StackOverFlow question thought it was excessively hacky and "might contain a lot of bugs/strange behaviors".
Before we use this, I wanted to ask the experts:
- Is this exploiting some part of the language that is unsafe?
- Are we going to be sorry for using it?
- Am I trying too hard to make a static language act dynamic, or something like that?