Discovery of possible keywords for kwargs... Maybe we need Base.optional_keywords?

I think we need better tooling to discover optional keyword arguments. Maybe this needs to be dealt with in Base, rather than VSCode, Juno, etc.

Consider the following scenario:

In Makie, we have many plotting functions with different optional keyword arguments for each. You don’t want to specify default values for them so you have to use the some_plot(; kwargs...) syntax. But this doesn’t tell the user what they can use. Of course you can document all the keywords, but this is not accessible to any tooling, unless they parse the docstring looking for a specifically formatted list of possible keywords. Maybe you even need some dynamism here and want to change the possible keywords at runtime because of some conditions?

I think we need something along the lines of propertynames, which solves the same problem for arbitrary additional fields, only we need it for arbitrary keyword arguments.

Imagine you could define Base.optional_keywords(p::plot_function) = (:linewidth, :color, :markersize), would that not be helpful in a lot of cases?


That would indeed be awesome and save a lot of time. I‘m always digging in the documentation… Maybe one could even go a step further and display the possible values a keyword can take, like the different colormaps. That would be super comfortable. On the other hand it would already help to have full fledged up-to-date docs…

I’d like to see something like this, but it’s not trivial to get right. Consider

f(a::Int; b = 2, c = 3) = a+b+c
f(a::Float64; d = 4) = a+d

– how would you define Base.optional_keywords(f)? Would it maybe make sense to define at the method (well, function + argtypes) level? It might also make sense to have attach docstrings and type annotations to each keyword.

1 Like

You can actually already get that with the following:

julia> f(a::Int; b = 2, c = 3) = a+b+c
f (generic function with 1 method)

julia> f(a::Float64; d = 4) = a+d
f (generic function with 2 methods)

julia> Base.kwarg_decl(which(f, Tuple{Int}))
2-element Array{Any,1}:

julia> Base.kwarg_decl(which(f, Tuple{Float64}))
1-element Array{Any,1}:

Although it might make sense to provide an official API for that.

1 Like

Right, I think this is more about f(args...; kwargs...)-style function definitions where the kwarg validation happens inside the function or in some other method called further down the line.


Ah I see. The problem with that would be that this isn’t really possible in general, since you’re free to do whatever you want with keyword arguments, so there might be infinitely many possible kwargs. You might be able to do some tracing to figure this out in the more common cases, but I’d imagine this would get quite complicated and brittle pretty quickly. Maybe the better solution for something like plots would be a macro that expanded to all allowed keyword arguments, so every function only explicitely allowed certain kwargs. Then this would be easy to check.


The plotting package Gaston is an example. It defines a few keyword arguments, and any others are assumed to be gnuplot set commands. For example, plot(..., object = "ellipse at 0,0 size 2,2", ...) is given to gnuplot as set object ellipse at 0,0 size 2,2. You will not find the object keyword anywhere in Gaston’s source code.

This level of flexibility is awesome: gnuplot has hundreds of keywords, and having to include code for every single one would be a lot of boring, prone-to-error, useless busywork. More importantly, this decouples Gaston from gnuplot’s syntax: Gaston exposes the new features in the recently released gnuplot 5.4, without me having to change a single line of code.


The problem is when you have a kwargs function where you know the allowed keywords at a lower level. Then it would be nice for the top level function to access that information somehow and pass it to tooling so that keywords can be suggested and completed while typing, for example.

It’s really like propertynames, which we use in makie to inform people of all the possible plot attributes that are available, which are not fields of the plot objects but dict entries a couple levels down.