Why are scalars iterable when broadcasting would still work fine if they weren't?

This is a soft-question about an aspect of Julia’s design and history.

It’s a well-discussed point that scalars are iterable, in the sense that:

julia> 42[1], 42[], size(42), length(42)
(42, 42, (), 1)

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

I previously thought that a primary reason for this design is that it allows broadcasting expressions such as 10 .* (1:5). However, the broadcasting interface is nice enough to “just work” even if scalars didn’t satisfy the array interface. Take Symbols, for example, which aren’t iterable:

julia> ++(a, b) = (a, b)
++ (generic function with 1 method)

julia> :not_iterable .++ (1:3) # broadcasting "just works"
3-element Array{Tuple{Symbol,Int64},1}:
 (:not_iterable, 1)
 (:not_iterable, 2)
 (:not_iterable, 3)

This works because Broadcast.broadcastable(:symbol) == Ref(:symbol).

Is there some other reason that iterability of scalars is useful or convenient?

I understand that removing this feature would without good reason would be too disruptive. Rather, I’m curious about the history of this design decision. What were the original reasons / would the same design decision be made now?

Simple answer: Broadcast.broadcastable was implemented quite recently as compared to the iterable-ness of scalars — I believe it was even more recently than their most recent excision attempt.

1 Like

See Map vs list comprehension - #9 by stevengj

1 Like