# Iterable signature, revisited

**URL:** <https://discourse.julialang.org/t/iterable-signature-revisited/129647>\
**Category:** General Usage\
**Tags:** iterators\
**Created:** [June 4, 2025, 3:46pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647 "2025-06-04T15:46:10Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![Ken\_Williams](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ken_williams/32/5289_2.png) [@Ken\_Williams](https://discourse.julialang.org/u/Ken_Williams)\
**Post date:** [June 4, 2025, 3:46pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/1 "2025-06-04T15:46:10Z")

</div>

There have been a few threads over the years about how to declare a function argument as “iterable” somehow, for example in [Iterable type signature?](https://discourse.julialang.org/t/iterable-type-signature/64550) .

For me (at least in my latest situation) the important part isn’t that I declare something as iterable, it’s declaring _what kind of items the iteration returns_. In other words, I’m fine accepting a `Vector{T}`, or a `Base.Generator{T}`, or a `Tuple{T}`, or an `AbstractRange{T}`, or whatever, but it’s important to specify what that `T` is.

Maybe one option is to do “casting” inside the function body?

```julia
julia> function do_stuff(a, b)
           for (ai::Int, bi::Int) in zip(a, b)
               println("$ai: $bi")
           end
       end
do_stuff (generic function with 1 method)

julia> do_stuff([1, 2], [4, 5])
1: 4
2: 5

julia> do_stuff([1, 2], [4.0, 5.0])
1: 4
2: 5

julia> do_stuff([1, 2], ["4", "5"])
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64

```

I think that basically accepts the right inputs and raises an error otherwise, but I don’t love the type error being uncaught until execution time of that function. Any progress on declarations here that I’ve missed over the past few years, that would let me express that those arguments must return a specific type when iterated upon?

---

<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 4, 2025, 4:14pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/2 "2025-06-04T16:14:45Z")

</div>

> [@Ken\_Williams](#):
>
> I think that basically accepts the right inputs and raises an error otherwise, but I don’t love the type error being uncaught until execution time of that function.

Note that you can actually catch this error at compile time:

```julia-repl
julia> using JET

julia> function do_stuff(a, b)
           for (ai::Int, bi::Int) in zip(a, b)
               println("$ai: $bi")
           end
       end
do_stuff (generic function with 1 method)

julia> report_opt(do_stuff, (Vector{Int}, Vector{String}))
═════ 1 possible error found ═════
┌ do_stuff(a::Vector{Int64}, b::Vector{String}) @ Main ./REPL[18]:2
│ runtime dispatch detected: convert(Int, %69::String)
└────────────────────

```

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [June 4, 2025, 4:23pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/3 "2025-06-04T16:23:47Z")

</div>

The issue here is defining what `Iterable` means. You’d think that, it means `hasmethod(iterate, (T,)) -> true`. But alas, `iterate` is defined on all sorts of scalars, including `Int`. Also, someone could do some like

```julia
struct T end
Base.iterate(::T) = error("T is not Iterable")

```

And suddenly it has a method for Iterable.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [June 4, 2025, 4:33pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/4 "2025-06-04T16:33:20Z")

</div>

Many iterables simply don’t know what type they’ll return until they actually generate a value. They definitionally won’t know if they’re ok until runtime.

Adding implicit or explicit checks can be a good solution.

---

<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 4, 2025, 4:44pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/5 "2025-06-04T16:44:13Z")

</div>

> [@mrufsvold](#):
>
> But alas, `iterate` is defined on all sorts of scalars, including `Int`. Also, someone could do some like

That’s not a counter example though. `Int` _is_ an iterable object that returns itself.

```julia
julia> iterate(1)
(1, nothing)

julia> [x for x in 1]
0-dimensional Array{Int64, 0}:
1

```

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [June 4, 2025, 5:06pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/6 "2025-06-04T17:06:14Z")

</div>

It’s a counter example to the statement “The existence of the `iterate` method implies that an object is non-scalar/a container of elements” which I think is a reasonable definition of the word “Iterable” (even if it is not correct in the context of Julia).

---

<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 4, 2025, 5:37pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/7 "2025-06-04T17:37:21Z")

</div>

It’s not a counter example though. `Int`s **are** containers of elements in julia (specifically, containers of a single element). Julia is just a little weird in that “scalar” doesn’t necessarily mean the opposite of “container of elements”.

```julia-repl
julia> length(1)
1

julia> map(x -> 2x, 1)
2

julia> [x/2 for x in 1]
0-dimensional Array{Float64, 0}:
0.5

julia> collect(1)
0-dimensional Array{Int64, 0}:
1

```

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [June 4, 2025, 6:17pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/8 "2025-06-04T18:17:55Z")

</div>

Relevant FAQ entry:

- [What are the differences between zero-dimensional arrays and scalars?](https://docs.julialang.org/en/v1/manual/faq/#faq-array-0dim)

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [June 4, 2025, 6:20pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/9 "2025-06-04T18:20:47Z")

</div>

> [@Ken\_Williams](#):
>
> those arguments must return a specific type when iterated upon

NB: there’s a difference between declaring the type of a variable, and doing a typeassert on some expression. In your code example you did the former, but from your words it seems like you expect the latter. The difference is that a typeassert just asserts a type, throwing otherwise, while assigning to a variable with declared type runs `convert` first. Which one you want depends on what exactly you’re doing.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [June 4, 2025, 6:23pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/10 "2025-06-04T18:23:51Z")

</div>

> [@Ken\_Williams](#):
>
> declaring _what kind of items the iteration returns_

That’s what `eltype` does. However, for arbitrary iterators it should be viewed as defined on a best-effort basis, that is, `eltype(iterator)` might return `Any` even if all elements are of the same type.

The best practice is usually to just do a typeassert. Assuming you really need it.

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [June 5, 2025, 1:28am UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/11 "2025-06-05T01:28:08Z")

</div>

OP makes a list of examples and does not include any subtypes of `Number` which indicates to me that they mean `Iterable` in a colloquial sense (and in the sense used in other languages). Before programming in Julia, I certainly didn’t think about the number 1 as a container of numbers that only includes 1 but has no size.

I added my parenthetical aside at the end – “even though this is not true in Julia” because I’m aware that numbers are zero-dimensional array, but that’s in the FAQ because it is surprising when you first learn it. Or, at least, it was for me!

---

<div class="post-metadata">

**Author:** ![Ken\_Williams](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ken_williams/32/5289_2.png) [@Ken\_Williams](https://discourse.julialang.org/u/Ken_Williams)\
**Post date:** [June 10, 2025, 5:01pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/12 "2025-06-10T17:01:58Z")

</div>

Just a note - the discussion around numbers & whether they are themselves iterable is a bit of a red herring. In my _real_ case (which I perhaps simplified a bit too much here, trying to create a MWE) , the `eltype` of the iterables are a user-defined type `Foo` and so won’t have this particular issue to worry about. So I’m basically looking for a type-level assurance that the iterands (?) are `Foo` objects.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [June 10, 2025, 5:04pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/13 "2025-06-10T17:04:56Z")

</div>

> [@Ken\_Williams](#):
>
> So I’m basically looking for a type-level assurance that the iterands (?) are `Foo` objects.

I guess `Foo` is concrete? Just typeassert, for example, do `value = value::Foo`.

---

<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:** [June 10, 2025, 5:32pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/14 "2025-06-10T17:32:55Z")

</div>

> [@Ken\_Williams](#):
>
> So I’m basically looking for a type-level assurance that the iterands (?) are `Foo` objects.

Why not just define:

```julia
foo(itr) = _foo(itr, eltype(itr))
_foo(itr, ::Type{Foo}) = ...

```

which will throw a `MethodError` if `itr` doesn’t define `eltype` or that `eltype` isn’t `Foo`?

Are you worried about iterators like generators that don’t define `eltype`?

---

<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 10, 2025, 8:48pm UTC](https://discourse.julialang.org/t/iterable-signature-revisited/129647/15 "2025-06-10T20:48:11Z")

</div>

Well, he did explicitly say

> [@Ken\_Williams](#):
>
> In other words, I’m fine accepting a `Vector{T}`, or a `Base.Generator{T}`, or a `Tuple{T}`, or an `AbstractRange{T}`, or whatever, but it’s important to specify what that `T` is.
