# (Idiomatic) functions on vectors containing elements of various types

**URL:** <https://discourse.julialang.org/t/idiomatic-functions-on-vectors-containing-elements-of-various-types/81923>\
**Category:** New to Julia\
**Tags:** question, type, vector\
**Created:** [May 30, 2022, 4:24pm UTC](https://discourse.julialang.org/t/idiomatic-functions-on-vectors-containing-elements-of-various-types/81923 "2022-05-30T16:24:34Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![lvignoli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lvignoli/32/36760_2.png) [@lvignoli](https://discourse.julialang.org/u/lvignoli)\
**Post date:** [May 30, 2022, 4:24pm UTC](https://discourse.julialang.org/t/idiomatic-functions-on-vectors-containing-elements-of-various-types/81923/1 "2022-05-30T16:24:34Z")

</div>

Hello everyone,

I am fairly new to Julia. I love it so far, but I struggle a bit with the type system right now.

I have a collection of concrete types A,B,C, D … that implements an abstract type Z.  
I would like to construct functions that operate only on containers of a subset of these types, say A, B and C, such as

```julia
bar([A(), B(), C(), A(), A(), B()])

```

My naive guess was to define

```julia
Foo = Union{A,B,C}

```

and

```julia
function bar(v::Vector{T}) where T<:Foo
   for x in v
      println(x)
   end
end

```

but it does not work:

```julia
x = A()
y = B()

bar([x,y]) # ERROR: MethodError: no method matching bar(::Vector{Any})

```

It is easy to reproduce by defining `Foo = Union{Int,String}`.

My understanding from the error and my experiments is that the above definition work only when `x` and `y` are of the same subtype of `Foo`, either all `A`, or all `B` or all `C`.

Hence my question: **how to define a function on a vector containing elements of various types?**  
(Or using VarArg, or other “container” types)  
What is the right, idiomatic pattern? Which types worth to be aliased?

Thank you 🙂

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [May 30, 2022, 4:48pm UTC](https://discourse.julialang.org/t/idiomatic-functions-on-vectors-containing-elements-of-various-types/81923/2 "2022-05-30T16:48:12Z")

</div>

I think you may have some lingering state in your code - `bar(x,y)` will never call `bar(v::Vector{T}) where T<:Foo` because the latter expects only a single argument - a `Vector`, not two arguments.

If you got that `MethodError` from `bar([A(), B(), C(), A(), A(), B()])`, it’s possible that you got a `Vector{Any}` out of this, since julia has to find a common type for all of these, which may end up as `Any`.

Do you have an runnable, standalone example?

---

<div class="post-metadata">

**Author:** ![skleinbo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skleinbo/32/36080_2.png) [@skleinbo](https://discourse.julialang.org/u/skleinbo)\
**Post date:** [May 30, 2022, 4:58pm UTC](https://discourse.julialang.org/t/idiomatic-functions-on-vectors-containing-elements-of-various-types/81923/3 "2022-05-30T16:58:48Z")

</div>

> [@lvignoli](#):
>
> `bar(x,y)`

I suppose you meant `bar([x,y])`, otherwise you are not passing a vector.

The point is, that in the absence of a rule, `A` and `B` promote to `Any`. Julia doesn’t promote to Union types by default:

```julia
julia> promote_type(String, Int)
Any

```

It is easy to define an appropriate rule though:

```julia
julia> import Base: promote_rule # import to overload

julia> struct A end; struct B end; struct C end;

julia> Foo = Union{A,B,C}
Union{A, B, C}

julia> promote_rule(::Type{S}, ::Type{T}) where {S<:Foo, T<:Foo} = Foo
promote_rule (generic function with 125 methods)

julia> [A(),B(),C()]
3-element Vector{Union{A, B, C}}:
 A()
 B()
 C()

```

Now the function `bar` dispatches correctly

```julia
julia> bar([A(),B()])
A()
B()

julia> bar([A(),B(),"Spoiled!"])
ERROR: MethodError: no method matching bar(::Vector{Any})
Closest candidates are:
  bar(::Vector{T}) where T<:Union{A, B, C} at REPL[6]:1
Stacktrace:
 [1] top-level scope
   @ REPL[9]:1

```

---

<div class="post-metadata">

**Author:** ![lvignoli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lvignoli/32/36760_2.png) [@lvignoli](https://discourse.julialang.org/u/lvignoli)\
**Post date:** [May 30, 2022, 5:01pm UTC](https://discourse.julialang.org/t/idiomatic-functions-on-vectors-containing-elements-of-various-types/81923/4 "2022-05-30T17:01:26Z")

</div>

Sorry I forgot the brackets I meant `bar([x,y])`.

Here is a MWE

```julia
Foo = Union{Int,String}

function bar(v::Vector{T}) where {T<:Foo}
    for x in v
        println(x)
    end
end

bar([1, 2]) # fine
bar(["a", "b"]) # fine
bar([1, "a"]) # ERROR: MethodError: no method matching bar(::Vector{Any})

```

---

<div class="post-metadata">

**Author:** ![lvignoli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lvignoli/32/36760_2.png) [@lvignoli](https://discourse.julialang.org/u/lvignoli)\
**Post date:** [May 30, 2022, 5:08pm UTC](https://discourse.julialang.org/t/idiomatic-functions-on-vectors-containing-elements-of-various-types/81923/5 "2022-05-30T17:08:03Z")

</div>

Thank you for your detailed explanation of promotion, I was not aware of this concept, but it makes lot of sense that there is actually no good supertype fallback unless you specify it.

Is there another way to implement this pattern that does not rely on promotion?  
For example, is it possible to define an abstract type `S` that has only `A`, `B` and `C` for subtypes, and write function against this type?

(In my use case `A, `B`, `C`, … come from an external library.)

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [May 30, 2022, 5:10pm UTC](https://discourse.julialang.org/t/idiomatic-functions-on-vectors-containing-elements-of-various-types/81923/6 "2022-05-30T17:10:17Z")

</div>

> [@lvignoli](#):
>
> `bar([1, "a"]) # ERROR: MethodError: no method matching bar(::Vector{Any})`

```julia
julia> [1, "a"] |> typeof
Vector{Any} (alias for Array{Any, 1})

```

The most common supertype for `Int` and `String` is `Any` - julia only very rarely builds unions voluntarily. You can force the element type of the vector to be of a specific type though:

```julia
julia> const Foo = Union{Int,String}
Union{Int64, String}

julia> Foo[1, "a"] |> typeof
Vector{Union{Int64, String}} (alias for Array{Union{Int64, String}, 1})

```

Also, for type aliases like `Foo`, make sure they’re `const`.

> [@lvignoli](#):
>
> Is there another way to implement this pattern that does not rely on promotion?  
> For example, is it possible to define an abstract type `S` that has only `A` , `B` and `C` for subtypes, and write function against this type?

In the example you’ve given, the objects don’t have a common type, but in your OP you mentioned that they do - I’d suggest typing the container with that common type, which will allow the function you wrote to work. An alternative would be to write it like

```julia
function bar(v::Vector{<:Foo})
     # implementation
end

```

since you don’t actually need to access `T` and you allow your container to be of mixed type, not a single concrete type that every object must have.

---

<div class="post-metadata">

**Author:** ![lvignoli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lvignoli/32/36760_2.png) [@lvignoli](https://discourse.julialang.org/u/lvignoli)\
**Post date:** [May 30, 2022, 5:23pm UTC](https://discourse.julialang.org/t/idiomatic-functions-on-vectors-containing-elements-of-various-types/81923/7 "2022-05-30T17:23:00Z")

</div>

Thank you for you clear anwsers. The `T[x,y]` syntax is a nice addition to my Julia knowledge!

> [@Sukera](#):
>
> I’d suggest typing the container with that common type, which will allow the function you wrote to work. An alternative would be to write it like

This totally works, but the point is to restrict the scope of the `bar` so that it accepts only few of the subtypes of `Foo` as elements of the vector `v`.

---

<div class="post-metadata">

**Author:** ![Karthik-d-k](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/karthik-d-k/32/35438_2.png) [@Karthik-d-k](https://discourse.julialang.org/u/Karthik-d-k)\
**Post date:** [May 30, 2022, 6:14pm UTC](https://discourse.julialang.org/t/idiomatic-functions-on-vectors-containing-elements-of-various-types/81923/8 "2022-05-30T18:14:59Z")

</div>

By looking at the ex’s presented here, i tried as below to restrict the vector only to contain `subtypes of Z` →

```julia
abstract type Z end

struct A <: Z end
struct B <: Z end
struct C <: Z end

const Foo = Union{A, B, C}

function bar(v::Vector{<:Foo})
    for x in v
        println(x)
    end
end

bar(Foo[A(),B(),C()]) # fine
bar(Foo[1, 2]) # Error
bar(Foo["a", "b"]) # Error
bar(Foo[1, "a"]) # Error

```

Is this what is expected ?

**P.S :** Please note that I’m a beginner too, so i could be completely wrong (just wanted to try bcz this question was interesting to me 😉)
