polymorphismjuliamultiple-dispatch

What is the correct way to specify that a Julia function can only take Dicts/Arrays whose contents are of certain types?


Let's say I have a function that expects an dict as input. Within that, this function can only handle values of that dict that are in a certain Union of allowed types. For this argument, the input can be Number, String, or Bool:

allowed_types = Union{String, Int, AbstractFloat, Bool}

The function can also permit Dicts whose values are these allowed types (Dict{String,allowed_types}) or Arrays whose items are these types (Array{allowed_types,Int}) as they can be "disassembled" down in to one of those allowed types. (This can keep continuing downwards - so an array of arrays, etc)

full_allowed_types = Union{allowed_types, Dict{String,allowed_types}, Array{allowed_types,Int}}

I can then define my function as

function my_func(input::Dict{String,full_allowed_types})
...
end

How, then, do I structure my function arguments so that I can pass, I.E, my_func(Dict("a"=>"astr","b"=>1))? Normally that Dict(...) call results in a Dict{String,Any}, which doesn't work to call with my function as Any is not an allowed type.

The error I get with my current implementation is:

my_func(Dict("a"=>"astr","b"=>1))
ERROR: MethodError: no method matching my_func(::Dict{String,Any})
Closest candidates are:
  my_func(::Dict{String,Union{Bool, Int64, Dict{String,Union{Bool, Int64, AbstractFloat, String}}, AbstractFloat, Array{Union{Bool, Int64, AbstractFloat, String},Int64}, String}}) at <snip>/my_func.jl:41
Stacktrace:
 [1] top-level scope at none:0

I'm picturing this issue from a user standpoint, where the user would likely just create a dict using the default constructor, without considering what my_func wants to be "allowed" (meaning I don't expect them to call Dict{String,my_pkg.full_allowed_types}(...)).

Is the best option to just allow Any as the input to my_func and then throw an error if any of the elements don't fit with my allowed types as I iterate through the input?


Solution

  • function f(a::Dict{String, A}) where A <: Union{Int,String}
       println("Got elem type $A")
    end
    

    Usage:

    julia> f(Dict{String,Union{String,Int}}("a"=>"astr","b"=>1))
    Got elem type Union{Int64, String}
    

    Now if you want to make it convenient for a user you could add an additional function (However, the type conversion will come at a cost):

    function f(a::Dict{String,A}) where A
        @warn "Provided unsupported type of elements $A will try narrow it to Union{Int,String}"
        f(Dict{String,Union{Int,String}}(d))
    end
    

    Example usage:

    
    julia> f(Dict("a"=>"astr","b"=>1))
    ā”Œ Warning: Provided unsuported type of elements Any will try narrow it to Union{Int,String}
    ā”” @ Main REPL[31]:2
    Got elem type Union{Int64, String}