# KeyedTuple and inference

**URL:** <https://discourse.julialang.org/t/keyedtuple-and-inference/9059>\
**Category:** Data\
**Created:** [February 14, 2018, 6:53am UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059 "2018-02-14T06:53:21Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![bramtayl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bramtayl/32/3614_2.png) [@bramtayl](https://discourse.julialang.org/u/bramtayl)\
**Post date:** [February 14, 2018, 6:53am UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/1 "2018-02-14T06:53:21Z")

</div>

I wonder if you’ve tried with the named tuple implementation in Keys.jl (v0.0.1, not master). That’s built on tuples.

---

<div class="post-metadata">

**Author:** ![nalimilan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nalimilan/32/147_2.png) [@nalimilan](https://discourse.julialang.org/u/nalimilan)\
**Post date:** [February 14, 2018, 9:37am UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/2 "2018-02-14T09:37:08Z")

</div>

I’ve just tried, and it actually seems worse since for some reason inference isn’t able to figure out the type of `x.a`. Anyway, I don’t think it could change anything as the problem applies to all types due to invariance. Tuples are the exception since they are covariant, but as soon as you wrap them inside a `struct` with type parameters the problem reappears.

---

<div class="post-metadata">

**Author:** ![bramtayl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bramtayl/32/3614_2.png) [@bramtayl](https://discourse.julialang.org/u/bramtayl)\
**Post date:** [February 14, 2018, 2:02pm UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/3 "2018-02-14T14:02:59Z")

</div>

Hmm, I worked pretty hard getting it to be type stable. Oh well worth a try.

---

<div class="post-metadata">

**Author:** ![bramtayl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bramtayl/32/3614_2.png) [@bramtayl](https://discourse.julialang.org/u/bramtayl)\
**Post date:** [February 14, 2018, 2:06pm UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/4 "2018-02-14T14:06:36Z")

</div>

Oh wait if you’re on v0.0.1 its pre dot overloading. You access values like this:

`x[@key a]`

---

<div class="post-metadata">

**Author:** ![nalimilan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nalimilan/32/147_2.png) [@nalimilan](https://discourse.julialang.org/u/nalimilan)\
**Post date:** [February 14, 2018, 2:33pm UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/5 "2018-02-14T14:33:01Z")

</div>

I was using master.

---

<div class="post-metadata">

**Author:** ![bramtayl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bramtayl/32/3614_2.png) [@bramtayl](https://discourse.julialang.org/u/bramtayl)\
**Post date:** [February 14, 2018, 3:11pm UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/7 "2018-02-14T15:11:56Z")

</div>

Ok, well, this seems like it works on 0.7

```julia
struct Key{T <: Any} end
Key(x) = Key{x}()

Base.@pure argtail(x, rest...) = rest
tail(x) = argtail(x...)

Base.show(io::IO, ::Key{T}) where T = begin
    print(io, ".")
    print(io, T)
end

next_item(t::Tuple, reference_key::T, next_key::T) where T <: Key =
    first(t)
next_item(t::Tuple{}, reference_key::T, next_key::T) where T <: Key =
    error("No item after key $reference_key")
next_item(t::Tuple, reference_key::Key, next_key) = 
    get_key(t, reference_key)
next_item(t::Tuple{}, reference_key::Key, next_key) = 
    error("Key $reference_key not found")

get_key(t::Tuple, k::Key) = next_item(tail(t), k, first(t))
get_key(t::Tuple{}, k::Key) = error("Can't index an empty tuple")

Base.getproperty(t::Tuple, k::Symbol) = get_key(t, Key(k))

function test()
    x = (Key(:a), 1, Key(:b), 2)
    x.b
end
@code_warntype test()

```

Involves a bit of type piracy, but

---

<div class="post-metadata">

**Author:** ![bramtayl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bramtayl/32/3614_2.png) [@bramtayl](https://discourse.julialang.org/u/bramtayl)\
**Post date:** [February 14, 2018, 3:37pm UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/8 "2018-02-14T15:37:22Z")

</div>

Ok, sorry I keep making edits, but the implementation above is built on a plain tuple so maybe it could get around the issues

---

<div class="post-metadata">

**Author:** ![bramtayl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bramtayl/32/3614_2.png) [@bramtayl](https://discourse.julialang.org/u/bramtayl)\
**Post date:** [February 14, 2018, 5:25pm UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/9 "2018-02-14T17:25:37Z")

</div>

Maybe this implementation is slightly nicer:

```julia
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()

```

---

<div class="post-metadata">

**Author:** ![bramtayl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bramtayl/32/3614_2.png) [@bramtayl](https://discourse.julialang.org/u/bramtayl)\
**Post date:** [February 14, 2018, 5:35pm UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/10 "2018-02-14T17:35:22Z")

</div>

Here’s another possible implementation:

```julia
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) = get_key(tail(t), first(t), k)
get_key(t::Tuple{}, k::Key) = error("Key $k not found")
get_key(t::Tuple, any, k::Key) = get_key(t, k)
get_key(t::Tuple, keyed::Keyed{K, V}, key::Key{K}) where {K, V} = keyed.value

function test()
    x = (Keyed(:a, 1), Keyed(:b, "b"))
    x.a
end
@code_warntype test()

```

---

<div class="post-metadata">

**Author:** ![nalimilan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nalimilan/32/147_2.png) [@nalimilan](https://discourse.julialang.org/u/nalimilan)\
**Post date:** [February 14, 2018, 6:46pm UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/11 "2018-02-14T18:46:04Z")

</div>

But adding `getindex` methods for `Tuple` is a serious type piracy, so that’s not really a workable solution. :-/

---

<div class="post-metadata">

**Author:** ![bramtayl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bramtayl/32/3614_2.png) [@bramtayl](https://discourse.julialang.org/u/bramtayl)\
**Post date:** [February 14, 2018, 7:16pm UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/12 "2018-02-14T19:16:10Z")

</div>

No I guess not, unless we could get this into Base

---

<div class="post-metadata">

**Author:** ![bramtayl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bramtayl/32/3614_2.png) [@bramtayl](https://discourse.julialang.org/u/bramtayl)\
**Post date:** [February 14, 2018, 7:22pm UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/13 "2018-02-14T19:22:42Z")

</div>

It’s also not serious type piracy because it would throw an error otherwise, that is, it wouldn’t break anyone’s code. From what I understand this type piracy is allowed in stdlibs? It might be nice to get NamedTuples into a stdlib, so that we could eventually switch it out.

EDIT: nevermind, we need them for keyword arguments.

---

<div class="post-metadata">

**Author:** ![bramtayl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bramtayl/32/3614_2.png) [@bramtayl](https://discourse.julialang.org/u/bramtayl)\
**Post date:** [March 3, 2018, 6:17pm UTC](https://discourse.julialang.org/t/keyedtuple-and-inference/9059/15 "2018-03-03T18:17:36Z")

</div>

I’ve made some progress here. I’ve rehabilitated Keys.jl. Should work with a master of Keys, TypedBools, and RecurUnroll. Fully covariant, no type piracy, fully featured replacement for `NamedTuples`.
