Those are some great thoughts and thinking out of the box. I've been pondering over if this could be related to the ideas from Key-Path Member Lookup. There seems to be some overlap and we should consider if these two proposals could be different sides of a same, yet undefined, abstraction.
It's a really wild idea, but one could implement Lazy
as a @keyPathMemberLookup
type:
@keyPathMemberLookup
enum Lazy<Value> {
case uninitialized(() -> Value)
case initialized(Value)
init(initialValue: @autoclosure @escaping () -> Value) {
self = .uninitialized(initialValue)
}
var value: Value {
mutating get {
switch self {
case .uninitialized(let initializer):
let value = initializer()
self = .initialized(value)
return value
case .initialized(let value):
return value
}
}
set {
self = .initialized(newValue)
}
}
subscript<U>(keyPathMember keyPath: WritableKeyPath<T, U>) -> U {...}
}
In which case the declaration would be as simple as
var name: Lazy<String>
while still getting the underlying type API through the property.
name.isEmpty
The limitation of @keyPathMemberLookup
is that it does not support methods in the current proposal, but one would expect that to be possible in future.
There is also a question on how to do the memberwise initializer and if the value
should be exposed, but those seem to be solvable problems.