@propertyWrapper struct Memoize: DynamicProperty { var wrappedValue: Value { fatalError("To use @Memoize, call it as a function, passing the input value(s), and a closure to produce the value") } @State @Boxed private var cache: (key: Key, value: Value)? init(key keyType: Key.Type = Int.self) {} private func getMemoized(key: Key, produceValue: () throws -> Value) rethrows -> Value { if let cache, cache.key == key { return cache.value } let newValue = try produceValue() cache = (key: key, value: newValue) return newValue } func callAsFunction(_ key: Key, produceValue: () throws -> Value) rethrows -> Value { return try getMemoized(key: key, produceValue: produceValue) } func callAsFunction( _ key: any Hashable..., produceValue: () throws -> Value ) rethrows -> Value where Key == Int { var keyHasher = Hasher() for keyPart in key { keyHasher.combine(keyPart) } return try getMemoized(key: keyHasher.finalize(), produceValue: produceValue) } } @propertyWrapper fileprivate class Boxed { var wrappedValue: Wrapped init(wrappedValue: Wrapped) { self.wrappedValue = wrappedValue } }