# Consistency between `keys`, `values`, and `getindex` for `Pair` type

Consider

``````julia> nt = (;a = 10, b = 20)
(a = 10, b = 20)

julia> [(;k, v) for (k, v) in pairs(nt)]
2-element Vector{NamedTuple{(:k, :v), Tuple{Symbol, Int64}}}:
(k = :a, v = 10)
(k = :b, v = 20)
``````

The above suggests that the keys of `nt` are `Symbol`s. The values of `nt` are `Int`s. Indeed,

``````julia> keys(nt)
(:a, :b)

julia> values(nt)
(10, 20)
``````

When a collection is `getindex`ed with value in the key space, it produces a value in the value space:

``````julia> nt[:a]
10
``````

So far so good.

I do not see such consistency within the `Pair` type itself.

``````julia> p = pairs(nt)
pairs(::NamedTuple) with 2 entries:
:a => 10
:b => 20

julia> [(;k, v) for (k, v) in pairs(p)]
2-element Vector{NamedTuple{(:k, :v), Tuple{Symbol, Int64}}}:
(k = :a, v = 10)
(k = :b, v = 20)
``````

This suggests that the key space is `Symbol` and the value space is `Int`. However,

``````julia> keys(p)
(:a, :b)

julia> values(p) # hmm?
(a = 10, b = 20)

julia> p[:a]
10
``````

Does the TODO in the definition of `values(::Pairs)` refers to this issue?

1 Like

I was trying to express an invariant that I expected to be violated:

``````julia> function check_invariant(collection)
for k in keys(collection)
collection[k] in values(collection) || return false
end
return true
end
check_invariant (generic function with 1 method)
``````

And thankfully,

``````julia> check_invariant(nt)
true

julia> check_invariant(pairs(nt))
true
``````

I was confused because `values(pairs(nt))` happens to include some keys (as helpfully pointed out by @ericphanson on Slack), but I see now why there is no inconsistency, because of the way that `values(pairs(nt))` iterates.

I was updating some Julia code to not use `kwargs.data` to access the `NamedTuple` underlying splatted kwargs, and using `values` looked wrong, because it looked like `value(kwargs)` would not contain any key data (since `values(::NamedTuple)` does not either). This confused me.