Example use case:
copy!(; dst, src)
# usage:
copy!(src=A, dst=B)
copy!(dst=A, src=B)
Not actually suggesting changing copy!
it's just a function where there isn't actually an obvious argument order, so using keywords might be a good idea.
The problem is that copy!
is used in a lot of performance-critical scenarios.
You can implement required keywords now:
function foo(; bar=:required)
if bar == :required
throw(ArgumentError("bar keyword argument must be specified"))
end
...
end
In the case of copy!
, I'm concerned about the performance aspects as @timholy suggests.
Copy was just an example. I'm not proposing actually changing it.
I think this is a spurious feature. And it clashes a bit with how keyword args are implemented --- they cannot _actually_ be required, all you can do is throw an error if they aren't passed. So for example the method copy!()
would appear to exist but it only throws an error.
I believe @vtjnash pointed out that you can use a throw
as a default value to get this effect, f(; req=error("req argument is required")
. I think that's the best way to get this feature, requiring no internal changes.
It would be nice to do this automatically when no default is supplied for a keyword argument.
Agreed. And this is different from dispatching on the names, which as Jeff pointed out, would clash with dispatch. It would also be nice to make this a type, so that we can generate a minimal amount of code to throw the error.
Ok, that all sounds good.
I don't know about making this a type, because I would want to be able to define the type of the keyword independently of whether it is required. e.g. f(; req::Int=requiredkeyword(:req))
. But it would be a lot nicer to just do f(; req::Int)
and have it automatically throw an error if not assigned.
@stevengj i mean that the error should have a type, not just a string, so that the cost of making the string is deferred until the user tries to print the exception
Ah yes, an UnassignedKeyword(kw::Symbol, T::Type) <: Exception
type makes sense here.
We can add this feature at any point since it's currently a syntax error, but the justification for having this be an actual built-in feature even though one can easily simulate it by having a default expression that throws an error is just to make the error messaging uniform and avoid repeated boilerplate of writing that code by hand.
There's also a slight consistency benefit – being able to explain that keyword arguments are just like positional arguments except that they are passed by name.
I'm writing a short article on default function arguments and having to note that they're always required for keyword arguments stuck out to me as something that "just happens to be that way" rather than stemming from a deeper design choice, at least as far as I've been able to make out.
In our case, the deeper design choice is that we don't dispatch on keyword arguments. In f(x, y)
both arguments are only "required" in the sense that if you don't pass them, another method (or no method) will be selected.
As discussed above, we could implement required keyword arguments by converting function f(x; k)
to function f(x; k = error("must pass k"))
but there would still be a difference in that you'd get a must pass k
error instead of a method error.
How about function foo(x; k = throw(ArgumentError("must pass k")))
?
I think that's fine: I never said this should be a no method error, it clearly shouldn't. It should either be an ArgumentError
or something like that.
In the meantime, I use a little macro
"""Convenience macro for requiring keyword arguments
``jldoctest
julia> f(x; @req(y), z = 3) = x + y + z
f (generic function with 2 methods)
julia> f(3)
ERROR: ArgumentError: y is required kwarg
Stacktrace:
[1] f(::Int64) at ./REPL[29]:1
julia> f(3, y=2)
8
``
"""
macro req(kwd::Symbol)
skwd = "$kwd is required kwarg"
argerror = :(throw(ArgumentError($(esc(skwd)))))
Expr(:kw, kwd, argerror)
end
Most helpful comment
In the meantime, I use a little macro