# What's the best way to dispatch on an \_iterable\_ trait?

**URL:** https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084
**Category:** New to Julia
**Tags:** traits
**Created:** [June 26, 2020, 12:51am UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084 "2020-06-26T00:51:45Z")
**Posts on this page:** 16
**Page:** 1

<div class="post-metadata">

### Author: ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)
#### Post date: [June 26, 2020, 12:51am UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/1 "2020-06-26T00:51:45Z")

</div>

I find that it’s quite common for me to write a function that requires the input to be iterable. But I don’t think there is a good way to restrict it to that. I think I would love to see a trait system built into Julia e.g.

```julia
fn(x::Symbol) = fn((x,))

function fn(x_itr:::IterableTrait)
   ## do something to each success element of `x_itr`
end

```

What’s the best way to achieve what I want so far in your opinion.

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [June 26, 2020, 1:15am UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/2 "2020-06-26T01:15:31Z")

</div>

The best way to my knowledge is [GitHub - oxinabox/Tricks.jl: Cunning tricks though the julia compiler internals](https://github.com/oxinabox/Tricks.jl). The `iterable` thing is actually the motivating example for the package. There are heavy caveats, but the package _does_ appear to work well and be fairly robust for this.

```julia
using Tricks: static_hasmethod
struct Iterable end
struct NonIterable end

isiterable(::Type{T}) where {T} = static_hasmethod(iterate, Tuple{T}) ? Iterable() : NonIterable()

fn(x::T) where {T} = fn(isiterable(T), x)
fn(::Iterable, x) = map(x -> (x, x), x)  
fn(::NonIterable, x) = (x,)

fn(:hi), fn(fn(:hi))

#+RESULTS:
: ((:hi,), ((:hi, :hi),))

```

```julia
@btime fn(:hi)

#+RESULTS:
: 1.329 ns (0 allocations: 0 bytes)
: (:hi,)

```

---

<div class="post-metadata">

### Author: ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)
#### Post date: [June 26, 2020, 1:36am UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/3 "2020-06-26T01:36:17Z")

</div>

I am kinda warming to the syntax of

```julia
fn(x::Type1:::Trait1)
fn(x::Union{Type1, Type2}:::Both{Trait1, Trait2})

```

---

<div class="post-metadata">

### Author: ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)
#### Post date: [June 26, 2020, 7:08am UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/4 "2020-06-26T07:08:52Z")

</div>

> [@xiaodai](#):
>
> What’s the best way to achieve what I want so far in your opinion.

Just document that it should be iterable and accept any argument.

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [June 26, 2020, 7:34pm UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/5 "2020-06-26T19:34:03Z")

</div>

While it wasn’t clear in the prose, the pseudo-code shown seems to imply that the OP wanted to dispatch on whether something is iterable and act differently depending on whether or not it’s iterable, in which case simply duck typing won’t work.

---

<div class="post-metadata">

### Author: ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)
#### Post date: [June 26, 2020, 8:05pm UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/6 "2020-06-26T20:05:08Z")

</div>

Why would you want to do that? Seems like that should be two different functions since they likely do completely different things.

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [June 26, 2020, 8:15pm UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/7 "2020-06-26T20:15:19Z")

</div>

🤷‍♂️

I don’t think it’s too hard to imagine cases where you care whether or not an input is has properties like being iterable or being callable and I don’t think telling people “don’t care about that” is particularly helpful (though one may often be right that there’s a better solution that doesn’t involve doing this).

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 27, 2020, 7:04am UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/8 "2020-06-27T07:04:15Z")

</div>

> [@Mason](#):
>
> I don’t think telling people “don’t care about that” is particularly helpful

I think the message is that trying to design an API which does something different depending on whether the input is iterable (callable etc) is the wrong way to go about it. Eg an imaginary

```julia
function do_stuff(thing)
    if is_iterable(thing)
        collect(thing)
    elseif is_callable(thing)
        thing()
    else
        fallback(thing)
    end
end

```

can be a very confusing design, regardless of whether it is done with traits or not.

Technically a trait for something supporting the `iterate` protocol would be very easy to add to the language, but I suspect that one of the reasons this has not happened is that usually there is a better way to organize code.

---

<div class="post-metadata">

### Author: ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)
#### Post date: [June 27, 2020, 7:30am UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/9 "2020-06-27T07:30:06Z")

</div>

FYI, there was a similar discussion in

> <https://github.com/JuliaLang/julia/pull/34296>
>
> Can we remove the restriction \`::Function\` in \`merge\[!\](combine::Function, d::Ab…stractDict, others::AbstractDict...)\`? Was this there to disambiguate the case where the first argument is \`AbstractDict\` but also is callable? Or was it there to allow \`merge\[!\](d::MyDict, ::Any)\`?

which introduced `mergewith(f, dicts...)` that superseded `merge(f, dicts...)`. This is because `merge(f, dicts...)` cannot be robustly distinguished from `merge(dicts...)` since any type can implement the call operator. I like how [Jeff summarized it](https://github.com/JuliaLang/julia/pull/34296#issuecomment-575299420):

> With multiple dispatch, just as the meaning of a function is important to get consistent, it’s also important to get the meanings of the argument slots consistent when possible. It’s not ideal for a function to treat an argument in a fundamentally different way based on its type

---

<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: [June 27, 2020, 1:14pm UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/10 "2020-06-27T13:14:15Z")

</div>

> [@tkf](#):
>
> it’s also important to get the meanings of the argument slots consistent when possible. It’s not ideal for a function to treat an argument in a fundamentally different way based on its type

However, this is often the case in base julia. E.g.

```julia
  strip(pred, str::AbstractString) -> SubString
  strip(str::AbstractString, chars) -> SubString

```

basically does opposite things depending on which argument is a `String`.

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 27, 2020, 1:53pm UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/11 "2020-06-27T13:53:34Z")

</div>

Yes, this is unfortunate, but it can only be changed with Julia 2.0.

An issue collecting these would make sense.

---

<div class="post-metadata">

### Author: ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)
#### Post date: [June 27, 2020, 10:13pm UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/12 "2020-06-27T22:13:23Z")

</div>

The use-case I had in mind was simply so that `fn` when applied to a non-iterable will not run and just generate an error. That’s all.

---

<div class="post-metadata">

### Author: ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)
#### Post date: [June 27, 2020, 11:33pm UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/13 "2020-06-27T23:33:08Z")

</div>

If the object doesn’t implement `iterate`, it’ll throw an error anyway.

If it is about giving a good error message, something like `Base.Experimental.register_error_hint` would be a better approach. Not sure if it’s usable here though.

---

<div class="post-metadata">

### Author: ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)
#### Post date: [July 4, 2020, 3:15pm UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/14 "2020-07-04T15:15:05Z")

</div>

Self plug here 🙂

If you use [BinaryTraits.jl](https://github.com/tk3369/BinaryTraits.jl), then you can statically verify the types assigned to the `Iterable` trait. So you would find any implementation problem during development rather than at runtime.

An example can be found here:  
[https://github.com/tk3369/BinaryTraits.jl/blob/master/examples/iteration\_indexing.jl#L6-L29](https://github.com/tk3369/BinaryTraits.jl/blob/master/examples/iteration_indexing.jl#L6-L29)

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [July 4, 2020, 3:24pm UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/15 "2020-07-04T15:24:53Z")

</div>

This still requires that the user go and assign any type they might accept with the `Iterable` trait though right? I think the OP wants to just automatically detect if something is iterable and dispatch on that.

---

<div class="post-metadata">

### Author: ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)
#### Post date: [July 4, 2020, 3:38pm UTC](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/16 "2020-07-04T15:38:30Z")

</div>

That’s right. OP was also thinking about [error reporting](https://discourse.julialang.org/t/whats-the-best-way-to-dispatch-on-an-iterable-trait/42084/12) and so I am just suggesting a different approach for consideration.
