# Matching any vector in function dispatch

**URL:** https://discourse.julialang.org/t/matching-any-vector-in-function-dispatch/113075
**Category:** New to Julia
**Tags:** function
**Created:** [April 17, 2024, 2:33pm UTC](https://discourse.julialang.org/t/matching-any-vector-in-function-dispatch/113075 "2024-04-17T14:33:52Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![suvayu](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/suvayu/32/208553_2.png) [@suvayu](https://discourse.julialang.org/u/suvayu)
#### Post date: [April 17, 2024, 2:33pm UTC](https://discourse.julialang.org/t/matching-any-vector-in-function-dispatch/113075/1 "2024-04-17T14:33:52Z")

</div>

I’m having trouble with function dispatch where I want to have two different implementations when the values in a dictionary are vectors or not

These work as I would expect:

```julia
function fn(val::Vector{Any})
    @info "fn, any vector"
    @show typeof(val)
end

function fn(val::Vector)
    @info "fn, vector"
    @show typeof(val)
end

function fn(val)
    @info "fn, scalar"
    @show typeof(val)
end

fn(1)
# [ Info: fn, scalar
# typeof(val) = Int64

fn(true)
# [ Info: fn, scalar
# typeof(val) = Bool

fn("foo")
# [ Info: fn, scalar
# typeof(val) = String

fn([1, 2])
# [ Info: fn, vector
# typeof(val) = Vector{Int64}

fn([true, false])
# [ Info: fn, vector
# typeof(val) = Vector{Bool}

fn(["foo", "bar"])
# [ Info: fn, vector
# typeof(val) = Vector{String}

fn([1, "foo"])
# [ Info: fn, any vector
# typeof(val) = Vector{Any}

```

However in the examples below, I don’t understand how the vector versions of the functions are being picked.

```julia
function fn(val::Dict{Symbol,Vector{Any}})
    @info "fn, dict of any vector"
    @show typeof(val)
end

function fn(val::Dict{Symbol,Vector})
    @info "fn, dict of vector"
    @show typeof(val)
end

function fn(val::Dict)
    @info "fn, dict of scalar"
    @show typeof(val)
end

## as expected 
fn(Dict(:a => 1, :b => 2))
# [ Info: fn, dict of scalar
# typeof(val) = Dict{Symbol, Int64}

fn(Dict(:a => true, :b => false))
# [ Info: fn, dict of scalar
# typeof(val) = Dict{Symbol, Bool}

fn(Dict(:a => "foo", :b => "bar"))
# [ Info: fn, dict of scalar
# typeof(val) = Dict{Symbol, String}

fn(Dict(:a => [1] , :b => [2]))
# [ Info: fn, dict of scalar <- expected dict of vector
# typeof(val) = Dict{Symbol, Vector{Int64}}

fn(Dict(:a => [true], :b => [false]))
# [ Info: fn, dict of scalar <- expected dict of vector
# typeof(val) = Dict{Symbol, Vector{Bool}}

fn(Dict(:a => ["foo"], :b => ["bar"]))
# [ Info: fn, dict of scalar <- expected dict of vector
# typeof(val) = Dict{Symbol, Vector{String}}

fn(Dict(:a => [1, "foo"], :b => ["bar"]))
# [ Info: fn, dict of vector <- expected dict of any vector
# typeof(val) = Dict{Symbol, Vector}

```

Can someone help me understand what’s going on, and recommend how should I be typing this? When the dictionary values are vectors, I want to have slightly different behaviour.

---

<div class="post-metadata">

### Author: ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)
#### Post date: [April 17, 2024, 2:45pm UTC](https://discourse.julialang.org/t/matching-any-vector-in-function-dispatch/113075/2 "2024-04-17T14:45:20Z")

</div>

Subtyping with parametric types is slightly counter-intuitive. Take a look at

> **[Vector{Int} \<: Vector{Real} is false??? · JuliaNotes.jl](https://m3g.github.io/JuliaNotes.jl/stable/typevariance/)**
>
> Documentation for JuliaNotes.jl.

---

<div class="post-metadata">

### Author: ![Sevi](https://avatars.discourse-cdn.com/v4/letter/s/c67d28/32.png) [@Sevi](https://discourse.julialang.org/u/Sevi)
#### Post date: [April 17, 2024, 7:15pm UTC](https://discourse.julialang.org/t/matching-any-vector-in-function-dispatch/113075/3 "2024-04-17T19:15:45Z")

</div>

Great explanation, thanks for sharing the link!

I just wanted to add a link to the relevant section of the [docs](https://docs.julialang.org/en/v1/manual/types/#man-parametric-composite-types), as well as a suggestion for solving the original issue:

```julia
# Fallback for "dict with symbol as key and anything as values"
fn(val::Dict{Symbol,T}) where T

# "dicts with symbols as keys and values that are a Vector of any kind (not just `Vector{Any}`!)
fn(val::Dict{Symbol,V}) where V <: Vector

# "dicts with symbols as keys and values that are `Vector{Any}` (note that this is more specific than the above)"
fn(val::Dict{Symbol,Vector{Any}})

```

I’m not sure whether this is what you want though.

* * *

The reason why your last example doesn’t work is (in my opinion) also because of a small gotcha:

```julia-repl
julia> Dict(:a => [1, "foo"], :b => ["bar"])
Dict{Symbol, Vector} with 2 entries:
  :a => Any[1, "foo"]
  :b => ["bar"]

```

The resulting dict contains one `Vector{Any}` and one `Vector{String}` and Julia decides to specify `Vector` as the common supertype and `Dict` parameter. _However_, there are cases, where the result is (perhaps) the one you might expect:

```julia-repl
julia> [[1, "foo"], ["bar"]]
2-element Vector{Vector{Any}}:
 [1, "foo"]
 ["bar"]

```

Although it looks like the same situation as with the dict above, Julia tries to make the eltype of the vector concrete here and converts the second element to a `Vector{Any}`. I don’t know why, but I guess it’s for better performance (?).

* * *

There is also a small gotcha when it comes to placing the type variables:

```julia-repl
julia> Dict{Symbol, Vector{Int}} <: Dict{Symbol, Vector{I}} where I <: Integer
true

julia> Dict{Symbol, Vector{Int}} <: Dict{Symbol, Vector{I} where I <: Integer}
false

```

The two types on the right-hand side look very similar, but they are different: the first case, the `I` is a type variable of the whole type and can be any subtype of `Integer`, so the expression is something like `Union{Dict{Symbol, Vector{Int}}, Dict{Symbol, Vector{Int32}}, Dict{Symbol, Vector{UInt32}}, ...}`. The type on the left-hand side is contained in this union.

But in the second case, the variable `I` is “bound” to the second parameter of the `Dict` and would translate to something like `Dict{Symbol, Union{Vector{Int}, Vector{UInt32}, ...}}`. Because of type invariance, the type on the left-hand side is not a subtype of this, although of course `Vector{Int} <: Vector{I} where I <: Integer`

This is why your `Dict{Symbol, Vector}` is _almost_ the right thing (`Vector` is essentially `Vector{T} where T`), but it doesn’t behave the same as `Dict{Symbol, Vector{T}} where T` where the type variable can apply to the whole dict type.

* * *

Another approach might be to somehow split the logic into a separate function call, i.e. have `fn(val::Dict{Symbol,T}) where T` as a generic function and either manually select which types inside the function with `T isa Vector` etc. or by writing an “inner function” that takes the type as argument. Then you could use the first function you wrote above which behaves as you would expect. You can wrap the types in a `Type` (see [here](https://docs.julialang.org/en/v1/manual/types/#man-typet-type) for more explanations)

```julia
function fn(x::Dict{Symbol, T}) where T
    ...
    inner(T, ...)
    ...
end 

function inner(::Type{T}, ...) where T <: Vector
    ...
end

```

For nested parametric types this may or may not make it easier to wrap one’s head around the path that multiple dispatch takes.

---

<div class="post-metadata">

### Author: ![suvayu](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/suvayu/32/208553_2.png) [@suvayu](https://discourse.julialang.org/u/suvayu)
#### Post date: [April 18, 2024, 8:57am UTC](https://discourse.julialang.org/t/matching-any-vector-in-function-dispatch/113075/4 "2024-04-18T08:57:38Z")

</div>

Both of you thanks a lot!

@gdalle your notes are amazing, it was invaluable to understand the concept. I was completely misunderstanding how container types work.

@Sevi your comment on the placement of the type variable was the final missing piece of understanding for me. Also thanks for the alternative suggestions. I’ll see what works best for my actual use case.

For completeness, modifying the MWE above like this makes everything work as intended:

```julia
function fn(val::Dict{Symbol,Vector{T} where T})
    @info "fn, dict of any vectors (also mixed types)"
    @show typeof(val)
end

function fn(val::Dict{Symbol,Vector{T}}) where T <: Union{Int64, String, Bool}
    @info "fn, dict of vector containing T ∈ union"
    @show typeof(val)
end

function fn(val::Dict{Symbol, T}) where T
    @info "fn, dict of scalar"
    @show typeof(val)
end

fn(Dict(:a => 1, :b => 2))
# [ Info: fn, dict of scalar
# typeof(val) = Dict{Symbol, Int64}

fn(Dict(:a => true, :b => false))
# [ Info: fn, dict of scalar
# typeof(val) = Dict{Symbol, Bool}

fn(Dict(:a => "foo", :b => "bar"))
# [ Info: fn, dict of scalar
# typeof(val) = Dict{Symbol, String}

fn(Dict(:a => [1] , :b => [2]))
# [ Info: fn, dict of vector containing T ∈ union
# typeof(val) = Dict{Symbol, Vector{Int64}}

fn(Dict(:a => [true], :b => [false]))
# [ Info: fn, dict of vector containing T ∈ union
# typeof(val) = Dict{Symbol, Vector{Bool}}

fn(Dict(:a => ["foo"], :b => ["bar"]))
# [ Info: fn, dict of vector containing T ∈ union
# typeof(val) = Dict{Symbol, Vector{String}}

fn(Dict(:a => [1, "foo"], :b => ["bar"]))
# [ Info: fn, dict of any vectors (also mixed types)
# typeof(val) = Dict{Symbol, Vector}

```

---

<div class="post-metadata">

### Author: ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)
#### Post date: [April 18, 2024, 8:59am UTC](https://discourse.julialang.org/t/matching-any-vector-in-function-dispatch/113075/5 "2024-04-18T08:59:19Z")

</div>

> [@suvayu](#):
>
> @gdalle your notes are amazing, it was invaluable to understand the concept. I was completely misunderstanding how container types work.

Not my notes, all credit goes to @lmiq 😉
