La razón por la que esto sucede es que los argumentos de palabras clave salpicados no se almacenan en una tupla con nombre de forma predeterminada. Podemos ver cómo se almacenan así:
julia> g(;kwargs...) = kwargs
g (generic function with 1 method)
julia> g(a=1)
pairs(::NamedTuple) with 1 entry:
:a => 1
julia> g(a=1) |> typeof
Base.Iterators.Pairs{Symbol,Int64,Tuple{Symbol},NamedTuple{(:a,),Tuple{Int64}}}
Por lo tanto, los kwargs salpicados se almacenan como algún tipo de objeto iterador. Sin embargo, podemos convertir fácilmente ese kwargs
iterador en un NamedTuple de esta manera: (;kwargs...)
y luego acceder a él de la manera que esperaríamos, por lo que su ejemplo se traduciría en
julia> f(args...; kwargs...) = (;kwargs...).x
f (generic function with 1 method)
julia> f(x=1, y=2)
1
Por supuesto, la forma más idiomática de hacer esto sería escribir la función como
julia> f(args...; x, kwargs...) = x
f (generic function with 1 method)
julia> f(x=1, y=2)
1
pero esto supone que conoce el nombre al que desea acceder ( x
) en el momento en que escribe la función.
Una breve nota al margen: si volvemos a nuestro ejemplo de g(;kwargs...) = kwargs
, podemos pedir los nombres de campo del objeto iterador que se devolvió de la siguiente manera:
julia> g(x=1, y=2) |> typeof |> fieldnames
(:data, :itr)
¿Qué es este data
campo?
julia> g(x=1, y=2).data
(x = 1, y = 2)
¡Ajá! así que en realidad podemos obtener los kwargs como una tupla nombrada usando eso, es decir f(;kwargs...) = kwargs.data.x
, funcionaría, pero no recomendaría este enfoque ya que parece depender de un comportamiento indocumentado, por lo que puede ser un mero detalle de implementación que no se garantiza que sea estable a través de las versiones de julia.