jw3126 / Setfield.jl

Update deeply nested immutable structs.
Other
167 stars 17 forks source link

Type-stable indexing of tuples #59

Closed tkf closed 5 years ago

tkf commented 5 years ago

Indexing tuple with IndexLens is currently not type sable:

julia> @code_warntype get((1, 2.0), @lens _[1])
Body::Union{Float64, Int64}
1 ─ %1 = (Base.getfield)(l, :indices)::Tuple{Int64}
│   %2 = (getfield)(%1, 1)::Int64
│   %3 = (Base.getfield)(obj, %2, $(Expr(:boundscheck)))::Union{Float64, Int64}
└──      return %3

How about adding (say) ConstIndexLens that stores the index in the type parameter? I think @lens _[$1] would be a good sugar for this.

tkf commented 5 years ago

I realized that it's a bit tricky to define set for a generic object. For tuples, it's easy:

struct ConstIndexLens{I} <: Lens end

Base.@propagate_inbounds Setfield.get(obj, ::ConstIndexLens{I}) where I = obj[I]

@generated function Setfield.set(obj::Tuple, ::ConstIndexLens{I}, val) where I
    args = map(1:length(obj.types)) do n
        n == I ? :val : :(obj[$n])
    end
    quote
        ($(args...),)
    end
end

Maybe we can just fall-back to IndexLens for non-tuples.