Rationale for kwargs as Iterators.Pairs

I’m curious why the keyword arguments are available wrapped in Iterators.Pairs in the function body:

function f(; kwargs...)
    # kwargs is a NamedTuple wrapped in an Iterators.Pairs
end

Is it legacy cruft from a time when named tuple were not so nice? Because to me it seems simpler and more useful to expose the named tuple directly.

Simpler: named tuples are well known. Users don’t need to learn another collection type. For key-value iteration we would write pairs(kwargs) which seems better to me (more explicit, and it reinforces the good pattern of using pairs where appropriate).

More useful: because named tuples have a richer API. For example I can get the list of values with values(nt). Here I need the awkward and counter-intuitive values(values(kwargs)).

3 Likes

The disadvantage is that a named tuple would require a different compilation for every distinct combination of kwargs passed in.

Couldn’t specializing on kwargs be an advantage?

How would it make a difference? Currently the function body gets an Iterators.Pairs of different type for each distinct combination of kwargs passed in:

julia> function f(; kwargs...)
           println(typeof(kwargs))
       end
f (generic function with 2 methods)

julia> f(a=1, b=2)
Base.Iterators.Pairs{Symbol, Int64, Tuple{Symbol, Symbol}, NamedTuple{(:a, :b), Tuple{Int64, Int64}}}

we don’t specialize on kwargs either way I think

1 Like