# Dispatch over element type

**URL:** <https://discourse.julialang.org/t/dispatch-over-element-type/93769>\
**Category:** General Usage\
**Tags:** traits\
**Created:** [January 30, 2023, 1:08pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769 "2023-01-30T13:08:59Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![iago-lito](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iago-lito/32/31924_2.png) [@iago-lito](https://discourse.julialang.org/u/iago-lito)\
**Post date:** [January 30, 2023, 1:08pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/1 "2023-01-30T13:08:59Z")

</div>

I find the following code too restrictive:

```julia
function f(collection::Vector{Int64})
    for i in collection
        println("Integer: $i")
    end
end

function f(collection::Vector{String})
    for s in collection
        println("String: $s")
    end
end

f([4, 5, 6]) # Success.
f(["a", "b", "c"]) # Success.
f((4, 5, 6)) # Failure: collection type is not Vector.
f([Substring("abc", i, i) for i in range(1, 3)]) # Failure: element type is not String.

```

To make the methods above more flexible to type dispatching, I wish I could write the following:

```julia
function f(collection::C) where eltype(C) <: Integer
    for i in collection
        println("Integer: $i")
    end
end

function f(collection::C) where eltype(C) <: AbstractString
    for s in collection
        println("String: $s")
    end
end

```

Unfortunately, the above is not valid Julia code (yet?). Is is possible to achieve this sort of _“abstract covariant dispatching”_ I’m after? or _“sophisticated static trait bounds”_? If not, what’s a usual workaround? I would personally go for:

```julia
function f(collection)
    if eltype(collection) <: Integer
        f_for_integers(collection)
    elseif eltype(collection) <: AbstractString
        f_for_strings(collection)
    else
        throw(ArgumentError("Invalid type..."))
    end
end

function f_for_integers(collection)
    for i in collection
        println("Integer: $i")
    end
end

function f_for_strings(collection)
    for s in collection
        println("String: $s")
    end
end

```

… but this feels like too much explicit type checking for a statically typed language, is it?

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [January 30, 2023, 1:33pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/2 "2023-01-30T13:33:20Z")

</div>

One answer is

```julia
function f(collection)
    for x in collection
        do_scalar(x)
    end
end
do_scalar(x::Int) ...

```

---

<div class="post-metadata">

**Author:** ![Rex\_Wang](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rex_wang/32/35108_2.png) [@Rex\_Wang](https://discourse.julialang.org/u/Rex_Wang)\
**Post date:** [January 30, 2023, 1:38pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/3 "2023-01-30T13:38:27Z")

</div>

You can simply write

```julia
function f(collection::AbstractVector{T}) where T<: AbstractString
    for i in collection
        println("String: $i")
    end
end

```

or

```julia
function f(collection::AbstractVector{<:AbstractString})
    for i in collection
        println("String: $i")
    end
end

```

in short.

ADD:  
This only fixes the case for abstract type (the last one above).  
Be careful of the difference between `{<:AbstractString}` and `{AbstractString}`.

```julia
f(a::Vector{<:AbstractString}) = true
h(a::Vector{AbstractString}) = true
f([""]) # true
h([""]) # no method found

```

---

<div class="post-metadata">

**Author:** ![Rex\_Wang](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rex_wang/32/35108_2.png) [@Rex\_Wang](https://discourse.julialang.org/u/Rex_Wang)\
**Post date:** [January 30, 2023, 1:43pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/4 "2023-01-30T13:43:14Z")

</div>

Moreover, the Julia package `WhereTraits` might meet your requirement.

> **[GitHub - jolin-io/WhereTraits.jl: traits for julia: dispatch on whatever you...](https://github.com/jolin-io/WhereTraits.jl)**
>
> traits for julia: dispatch on whatever you want using where syntax - GitHub - jolin-io/WhereTraits.jl: traits for julia: dispatch on whatever you want using where syntax

```julia
julia> @traits function f(collection) where eltype(collection) <: Integer
           for i in collection
               println("Integer: $i")
           end
       end

julia> f([1,2,3])
Integer: 1
Integer: 2
Integer: 3

```

Note: There will have five methods for `f` by this way.

```julia
julia> methods(f)
# 5 methods for generic function "f":
...

```

---

<div class="post-metadata">

**Author:** ![PharmCat](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pharmcat/32/6953_2.png) [@PharmCat](https://discourse.julialang.org/u/PharmCat)\
**Post date:** [January 30, 2023, 1:47pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/5 "2023-01-30T13:47:06Z")

</div>

He want dispatch Tuple and Vectors…

If it is really matter to include tuples - you should take into account that !(Tuple \<: AbstractVector), but `isa((1,2,3), Tuple{Vararg{Int}})`, so you should make method for tuple of strings:

```julia
function f(collection::Tuple{Vararg{<:AbstractString}})
    for s in collection
        println("String: $s")
    end
end

```

---

<div class="post-metadata">

**Author:** ![iago-lito](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iago-lito/32/31924_2.png) [@iago-lito](https://discourse.julialang.org/u/iago-lito)\
**Post date:** [January 30, 2023, 2:39pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/6 "2023-01-30T14:39:13Z")

</div>

Hi and thank you for your feedback 🙂

> [@Oscar\_Smith](#):
>
> One answer is
> 
> ```julia
> function f(collection)
> for x in collection
> do_scalar(x)
> end
> end
> do_scalar(x::Int) ...
> 
> ```

Although this does work with the examples I have chosen, you can only generalize this approach to functions where `do_scalar` can be written (for example, functions with independent processing of every item). In other terms, this solution depends on the actual code within `f` in a way that sort of bypasses the actual dispatching problem ^ ^"

> [@Rex\_Wang](#):
>
> `function f(collection::AbstractVector{<:AbstractString})`

Yupe. But as you write, this does not dispatch over tuples or generators, only over collections in `subtypes(AbstractVectors)`.

> [@PharmCat](#):
>
> He want dispatch Tuple and Vectors…

Not exactly, since I would also expect `f(Set(range(5))` to work. This alternate collection type is neither a `Vector` nor a `Tuple`, but still, `eltype(C) <: Integer`. Is there no abstract julia type for collections and/or iterables?

> [@Rex\_Wang](#):
>
> the Julia package `WhereTraits` might meet your requirement.

This seems exactly powerful enough for my purpose 😃 Thank you! Can I first make sure that there is no built-in Julia idiom for that before I mark your post as the “solution”?

---

<div class="post-metadata">

**Author:** ![PharmCat](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pharmcat/32/6953_2.png) [@PharmCat](https://discourse.julialang.org/u/PharmCat)\
**Post date:** [January 30, 2023, 3:49pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/7 "2023-01-30T15:49:39Z")

</div>

> [@iago-lito](#):
>
> Is there no abstract julia type for collections and/or iterables?

No. As I know.

I suppose that using dispatch this way is not good idea by design. If you want to work only with collections you can check it `applicable(length, [1,2,3])`

If you want to dispatch by eltype you can try something like this:

```julia
function f(collection, ::Type{<:AbstractString})
    for i in collection
        println(i)
    end
end
function f2(collection)
    if applicable(length, collection) 
        f(collection, eltype(collection))
    end
end
a = ["s","ss", "sss"] 
f2(a)

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 30, 2023, 3:53pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/8 "2023-01-30T15:53:38Z")

</div>

> [@iago-lito](#):
>
> Unfortunately, the above is not valid Julia code (yet?). Is is possible to achieve this sort of _“abstract covariant dispatching”_ I’m after? or _“sophisticated static trait bounds”_? If not, what’s a usual workaround? I would personally go for:

If you want to support arbitrary iterables, I think you want to use traits for this:

```julia
f(collection) = _f(collection, Base.IteratorEltype(collection))
_f(collection, ::Base.EltypeUnknown) = ...fallback for unknown eltype...
_f(collection, ::Base.HasEltype) = _f(collection, eltype(collection))
_f(collection, ::Type{T}) where {T<:AbstractString} = ...string code....
_f(collection, ::Type{T}) where {T<:Integer} = ...integer code....

```

---

<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:** [January 30, 2023, 3:59pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/9 "2023-01-30T15:59:03Z")

</div>

> [@iago-lito](#):
>
> Can I first make sure that there is no built-in Julia idiom for that before I mark your post as the “solution”?

I’d say your original solution is pretty idiomatic. The branching in `f` is basically cost free, or rather eliminated entirely, since `eltype` (should) constant propagate. I’m pretty sure `@traits` generates something very similar, but have a look with `@macroexpand`.

---

<div class="post-metadata">

**Author:** ![iago-lito](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iago-lito/32/31924_2.png) [@iago-lito](https://discourse.julialang.org/u/iago-lito)\
**Post date:** [January 30, 2023, 6:01pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/10 "2023-01-30T18:01:12Z")

</div>

Hi all, and thank you again 🙂

> [@PharmCat](#):
>
> I suppose that using dispatch this way is not good idea by design.

Can you elaborate on why you think it’s not? For instance, I think I would agree with you if `f` was an internal function used in some performance-critic part of the code. But (and maybe it’s a useful piece of context indeed:) this is a public API. If I had to describe the design in a nutshell: _I don’t want to state unnecessary constraints on my function signature_.  
All I’m needing here is that `collection` yields items in an iterable fashion, _i.e._ `for i in collection` should not error. As a consequence, I would be unsatisfied to force my callers into using a `Vector` rather than anything they have at hand like a `Tuple`, a `Set` or a lazy iterator.  
This being said, and since performance is not a concern, I would agree that converting the received argument to some standard collection like `Vector` could be useful indeed:

```julia
f(collection) = f(Vector(collection))
f(collection::Vector{<:Integer}) = # implement for integers
f(collection::Vector{<:AbstractString}) = # implement for strings
f(::Vector) = throw(ArgumentError("Invalid element type.."))

```

But I find this somewhat unsatisfying because `Vector` is only introduced/allocated here for dispatching purpose. Dispatching should not influence implementation this way, right?

> [@skleinbo](#):
>
> The branching in `f` is basically cost free, or rather eliminated entirely, since `eltype` (should) constant propagate. I’m pretty sure `@traits` generates something very similar

This is also what I think. `if eltype(collection) <: Integer` is a good way to go if I’m not willing to include the third-party `@trait` macro here.

> [@stevengj](#):
>
> If you want to support arbitrary iterables, I think you want to use traits for this:
> 
> ```julia
> f(collection) = _f(collection, Base.IteratorEltype(collection))
> _f(collection, ::Base.EltypeUnknown) = ...fallback for unknown eltype...
> _f(collection, ::Base.HasEltype) = _f(collection, eltype(collection))
> _f(collection, ::Type{T}) where {T<:AbstractString} = ...string code....
> _f(collection, ::Type{T}) where {T<:Integer} = ...integer code....
> 
> ```

Oh, I didn’t know about those 😃 This is very close to the perfect solution because it’s semantically accurate and only uses native Julia concepts. Although it’s a little bit complicated for a small function with only a few expected possible element types, I would definitely go for it if the list of expected possible types needs to be large, or open. Maybe I need to read [that part](https://docs.julialang.org/en/v1/manual/methods/#Trait-based-dispatch) of the docs now?

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [January 30, 2023, 6:07pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/11 "2023-01-30T18:07:52Z")

</div>

> [@iago-lito](#):
>
> Although it’s a little bit complicated for a small function with only a few expected possible element types

It’s pretty short actually, if you ignore unknown eltypes:

```julia
f(collection) = _f(collection, eltype(collection))
_f(collection, ::Type{<:AbstractString}) = ...string code....
_f(collection, ::Type{<:Integer}) = ...integer code....

```

I think this is the most direct approach.

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [January 30, 2023, 8:25pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/12 "2023-01-30T20:25:38Z")

</div>

Yes, this is a good solution and probably rather close to the code that one of the `trait` libraries would generate.  
In any case: What exactly are you trying to do, i.e., why would your function need to behave differently for the element type yet needs access to the whole iterable?  
Just asking, because some higher-order function might already do the trick. You already suggested that `map(do_scalar, collection)` would not be enough. There is also reduce though, which is probably the most general type of processing you can do when the actual collection type is unknown, i.e., you can only rely on iterating over it:

```julia
reduce(combine, collection; init=initial)
# roughly equivalent to
begin
    res = initial
    for ele in collection
        res = combine(res, ele)
    end
    res
end

```

---

<div class="post-metadata">

**Author:** ![PharmCat](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pharmcat/32/6953_2.png) [@PharmCat](https://discourse.julialang.org/u/PharmCat)\
**Post date:** [January 30, 2023, 8:38pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/13 "2023-01-30T20:38:04Z")

</div>

> [@iago-lito](#):
>
> Can you elaborate on why you think it’s not? For instance, I think I would agree with you if `f` was an internal function used in some performance-critic part of the code.

Yep. I think `eltype(collection)` can decrease performance in critical parts. Also all such types: `Tuple{String}`, `Tuple{String, String}` is a different types and you will have different methods for all of them and in some cases it can lead to long compile time.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 30, 2023, 8:46pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/14 "2023-01-30T20:46:18Z")

</div>

> [@PharmCat](#):
>
> Yep. I think `eltype(collection)` can decrease performance in critical parts.

It shouldn’t — this is evaluated at compile-time if `collection` is type-inferred.

---

<div class="post-metadata">

**Author:** ![PharmCat](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pharmcat/32/6953_2.png) [@PharmCat](https://discourse.julialang.org/u/PharmCat)\
**Post date:** [January 30, 2023, 8:54pm UTC](https://discourse.julialang.org/t/dispatch-over-element-type/93769/15 "2023-01-30T20:54:02Z")

</div>

> [@stevengj](#):
>
> `Base.HasEltype`

Also `Base.IteratorEltype(1) == Base.HasEltype()` and it can be critical in some ways, for example if you want return vector for collection and single element when provide `Int`.
