Allow function to ignore unsupported keyword arguments - julia

Is there a nice way of allowing a function to ignore unsupported keyword arguments?
fopts = [:kw1]
opts = Dict(:kw1=>:symb1, :kw2=>:symb2)
function f(; kw1 = :symb)
return kw1
end
f(;opts...) will throw a METHOD ERROR
I could wrap it in something like this, but then I still need to know which kwargs f will support?
function f2(fopts; kwargs)
f(; Dict(key=>get(opts, key, 0) for key in fopts)...)
end
Am I missing a way around this. Not that fussed if there is a performance penalty as I imagine their may need to be some kind of look up. Is there a good way of interrogating what kwargs f accepts programatically?

Is this what you want?
function g(; kw1 = :a, kw2 = :b, _whatever...)
return (kw1, kw2)
end
Now it works like this:
julia> g()
(:a,:b)
julia> g(kw1 = :c)
(:c,:b)
julia> g(kw2 = :d)
(:a,:d)
julia> g(kw2 = :e, kw1 = :f, kw3 = :boo)
(:f,:e)

Based on both #tim & #Gnimuc's comments one could define these two functions:
getkwargs(f) = methods(methods(f).mt.kwsorter).mt.defs.func.lambda_template.slotnames[4:end-4]
usesupportedkwargs2(f::Function, args...; kwargs...) = f(args...; Dict(key=>get(Dict(kwargs),key,0) for key in getkwargs(f))...)
But maybe there is a better way

Related

Get names of keywords for Julia methods

I have a function like
function f(a = 1; first = 5, second = "asdf")
return a
end
Is there any way to programatically return a vector with the names of the keyword arguments. Something like:
kwargs(f)
# returns [:first, :second]
I realise that this might be complicated by having multiple methods for a functionname. But I was hoping this would still be possible if the exact method is specified. For instance:
kwargs(methods(f).ms[1])
# returns [:first, :second]
Just use Base.kwarg_decl()
julia> Base.kwarg_decl.(methods(f))
2-element Vector{Vector{Symbol}}:
[]
[:first, :second]
If you need the first parameter a as well you could also try:
julia> Base.method_argnames.(methods(f))
2-element Vector{Vector{Symbol}}:
[Symbol("#self#")]
[Symbol("#self#"), :a]

In Julia: How to declare the type of a function parameter that is a function? [duplicate]

Can I add type information to arguments that are functions?
Consider the following example:
function f{T} (func, x::Int)
output = Dict{Int, Any}()
output[x] = func(x)
return output
end
I don't like that I have to say Any for the value type of the dictionary. I'd much rather do the following:
function f{T} (func::Function{Int->T}, x::Int)
output = Dict{Int, T}()
output[x] = func(x)
return output
end
Can I provide type hints of functions like this? I kind of want to say the following
f :: (Int -> T), Int -> Dict{Int, T}
Not currently. We may add something along those lines in the future, however.
This is not an answer to the main question, but more a really ugly workaround the Any in the Dict issue:
function f(func, x::Int)
T = code_typed(func, (Int,))[1].args[3].typ
output = Dict{Int, T}()
output[x] = func(x)
return output
end
That is probably not efficient and will probably work only on simple cases (which do not even include anonymous functions) like
>>> g(x) = x*2
>>> typeof(f(g, 1234))
Dict{Int64,Int64}
>>> h(x) = x > zero(x) ? x : nothing
>>> typeof(f(h, 1234))
Dict{Int64,Union(Int64,Nothing)}
EDIT:
This works better:
function f(func, x::Int)
[x => func(x)]
end
>>> dump( f(x->2x, 3) )
Dict{Int64,Int64} len 1
3: Int64 6

Check if a function has keywords arguments in Julia

Is there a way to check if a function has keywords arguments in Julia? I am looking for something like has_kwargs(fun::Function) that would return true if fun has a method with keyword arguments.
The high level idea is to build a function:
function master_fun(foo::Any, fun::Function, ar::Tuple, kw::Tuple)
if has_kwargs(fun)
fun(ar... ; kw...)
else
fun(ar...)
end
end
Basically, #Michael K. Borregaard's suggestion to use try-catch is correct and officially works.
Looking into the unofficial implementation details, I came up with the followng:
haskw(f,tup) = isdefined(typeof(f).name.mt,:kwsorter) &&
length(methods(typeof(f).name.mt.kwsorter,(Vector{Any},typeof(f),tup...)))>0
This function first looks if there is any keyword processing on any method of the generic function, and if so, looks at the specific tuple of types.
For example:
julia> f(x::Int) = 1
f (generic function with 1 method)
julia> f(x::String ; y="value") = 2
f (generic function with 2 methods)
julia> haskw(f,(Int,))
false
julia> haskw(f,(String,))
true
This should be tested for the specific application, as it probably doesn't work when non-leaf types are involved. As Michael commented, in the question's context the statement would be:
if haskw(fun, typeof.(ar))
...
I don't think you can guarantee that a given function has keyword arguments. Check
f(;x = 3) = println(x)
f(x) = println(2x)
f(3)
#6
f(x = 3)
#3
f(3, x = 3)
#ERROR: MethodError: no method matching f(::Int64; x=3)
#Closest candidates are:
# f(::Any) at REPL[2]:1 got unsupported keyword argument "x"
# f(; x) at REPL[1]:1
So, does the f function have keywords? You can only check for a given method. Note that, in your example above, you'd normally just do
function master_fun(foo, fun::Function, ar::Tuple, kw....)
fun(ar... ; kw...)
end
which should work, and if keywords are passed to a function that does not take them you'd just leave the error reporting to fun. If that is not acceptable you could try to wrap the fun(ar...; kw...) in a try-catch block.

julia-lang Check element type of arbitrarily nested array

How could I check the element type of a nested array assumming I don't know the level of nesting?:
julia> a = [[[[1]]]]
1-element Array{Array{Array{Array{Int64,1},1},1},1}:
Array{Array{Int64,1},1}[Array{Int64,1}[[1]]]
julia> etype(a)
Int64?
As it is often the case for type computations, a recursive approach works very well:
nested_eltype(x) = nested_eltype(typeof(x))
nested_eltype{T<:AbstractArray}(::Type{T}) = nested_eltype(eltype(T))
nested_eltype{T}(::Type{T}) = T
This has no runtime overhead:
julia> #code_llvm nested_eltype([[[[[[[[[[1]]]]]]]]]])
define %jl_value_t* #julia_nested_eltype_71712(%jl_value_t*) #0 {
top:
ret %jl_value_t* inttoptr (i64 140658266768816 to %jl_value_t*)
}
There is probably a clever way to do this with one line, but in the meantime, you can use recursive calls to eltype to do this inside a while loop:
function nested_eltype(x::AbstractArray)
y = eltype(x)
while y <: AbstractArray
y = eltype(y)
end
return(y)
end
Note that this works for nested arrays of any dimension, ie it doesn't just have to be Vector like the example in your question...
This is a generated version using .depth attribute:
#generated function etype{T}(x::T)
ex = :(x)
for i = 1:T.depth
ex = :($ex |> eltype)
end
ex
end
julia> a = [[[[1]]]]
1-element Array{Array{Array{Array{Int64,1},1},1},1}:
Array{Array{Int64,1},1}[Array{Int64,1}[[1]]]
julia> etype(a)
Int64

Can I add type information to arguments that are functions in Julia?

Can I add type information to arguments that are functions?
Consider the following example:
function f{T} (func, x::Int)
output = Dict{Int, Any}()
output[x] = func(x)
return output
end
I don't like that I have to say Any for the value type of the dictionary. I'd much rather do the following:
function f{T} (func::Function{Int->T}, x::Int)
output = Dict{Int, T}()
output[x] = func(x)
return output
end
Can I provide type hints of functions like this? I kind of want to say the following
f :: (Int -> T), Int -> Dict{Int, T}
Not currently. We may add something along those lines in the future, however.
This is not an answer to the main question, but more a really ugly workaround the Any in the Dict issue:
function f(func, x::Int)
T = code_typed(func, (Int,))[1].args[3].typ
output = Dict{Int, T}()
output[x] = func(x)
return output
end
That is probably not efficient and will probably work only on simple cases (which do not even include anonymous functions) like
>>> g(x) = x*2
>>> typeof(f(g, 1234))
Dict{Int64,Int64}
>>> h(x) = x > zero(x) ? x : nothing
>>> typeof(f(h, 1234))
Dict{Int64,Union(Int64,Nothing)}
EDIT:
This works better:
function f(func, x::Int)
[x => func(x)]
end
>>> dump( f(x->2x, 3) )
Dict{Int64,Int64} len 1
3: Int64 6

Resources