Maybe this implementation is slightly nicer:
struct Key{K} end
Key(s::Symbol) = Key{s}()
function Base.show(io::IO, k::Key{K}) where {K}
print(io, ".")
print(io, K)
end
struct Keyed{K, V}
value::V
end
Base.@pure Keyed(key::Symbol, value::V) where V = Keyed{key, V}(value)
function Base.show(io::IO, k::Keyed{K, V}) where {K, V}
print(io, K)
print(io, " = ")
print(io, k.value)
end
Base.@pure argtail(x, rest...) = rest
tail(x) = argtail(x...)
key_is(keyed::Keyed{K, V}, key::Key{K}) where {K, V} = true
key_is(any, k::Key) = false
value(k::Keyed) = k.value
Base.getproperty(t::Tuple, s::Symbol) = get_key(t, Key(s))
get_key(t::Tuple{}, k::Key) = error("Key $k not found")
function get_key(t::Tuple, k::Key) where {K, V}
first_item = first(t)
if key_is(first(t), k)
value(first_item)
else
get_key(tail(t), k)
end
end
function test()
x = (Keyed(:a, 1), Keyed(:b, "b"))
x.a
end
@code_warntype test()