Typing functions arguments representing tuples of custom structs

Hello,

Considering the following struct:

struct my_struct{A <: Function,B <: Function}
    a::A
    b::B
end

I would like to write a function func that accepts an OrderedDict which values are Tuples of an unknown amount of my_structs. I tried typing its arguments:

function func(od::OrderedDict{Any,Tuple{Vararg{my_struct{<:Function, <:Function }}}}) 
    # do stuff
    od
end

But if I try it:

using DataStructures
my_struct_1 = my_struct(Base.:+ , Base.:- )
my_struct_2 = my_struct(Base.:* , Base.:/ )

od = OrderedDict("a" => (my_struct_1 ,my_struct_2 ))

func(od)

I get:

ERROR: MethodError: no method matching func(::OrderedDict{String, Tuple{my_struct{typeof(+), typeof(-)}, my_struct{typeof(*), typeof(/)}}})
Closest candidates are:
  func(::OrderedDict{Any, Tuple{Vararg{my_struct{var"#s1", var"#s3"} where {var"#s1"<:Function, var"#s3"<:Function}, N} where N}}) at REPL[3]:1
Stacktrace:
 [1] top-level scope
   @ REPL[7]:1

How may I correctly type func's argument?

1 Like

maybe just don’t type it? Your keys are Any anyway.

The reason your current code doesn’t work I suspect is because Vararg requires all the args same type, my_struct_1 and my_struct_2 has different types

also the Any needs to be <:Any, working example:

julia> function func(od::T) where T <: Dict{<:Any, <:Tuple{Vararg{my_struct}}}
           # do stuff
           od
       end
func (generic function with 1 method)

julia> my_struct_1 = my_struct(Base.:+ , Base.:- )

julia> func(Dict("a" => (my_struct_1 ,my_struct_1 )))
Dict{String, Tuple{my_struct{typeof(+), typeof(-)}, my_struct{typeof(+), typeof(-)}}} with 1 entry:
  "a" => (my_struct{typeof(+), typeof(-)}(+, -), my_struct{typeof(+), typeof(-)}(+, -))

julia> my_struct_2 = my_struct(Base.:* , Base.:/ )

julia> func(Dict("a" => (my_struct_1 ,my_struct_2 )))
Dict{String, Tuple{my_struct{typeof(+), typeof(-)}, my_struct{typeof(*), typeof(/)}}} with 1 entry:
  "a" => (my_struct{typeof(+), typeof(-)}(+, -), my_struct{typeof(*), typeof(/)}(*, /))
1 Like

Thanks.

Could you (or someone) please explain (or refer me to an explanation of) why this works while directly tipying the arguments like the initial post doesn’t? I tried to read the docs but I couldn’t find the answer.

1 Like
julia> function func(od::Dict{<:Any, <:Tuple{Vararg{my_struct}}})
                  # do stuff
                  od
              end

is identical, moving it out was just clearer when I was working on it.

In fact, all you missed was <:Any, <:Tuple.., the two <: inside the parameter of Dict

1 Like

Thanks.

Sorry for the insistence, but then why does ::Dict{<:Any, <:Tuple{Vararg{my_struct}}} work and ::Dict{Any,Tuple{Vararg{my_struct}}} doesn’t? What does the <: do besides specifying that there should be a subtype of what is on the right?

1 Like
julia> Vector{String} <: Vector{Any}
false

julia> Vector{String} <: Vector{<:Any}
true

does this make it clear?

https://docs.julialang.org/en/v1/manual/types/#man-parametric-composite-types

people talk about this as “Julia’s type parameters are invariant”

2 Likes

https://m3g.github.io/JuliaNotes.jl/stable/typevariance/

2 Likes