Detect argument is an iterable or a "scalar"

Just Tuple would be a UnionAll (if tuple was not more primitive than other parametric types), and these are never isbits I think.

help?> UnionAll
search: UnionAll

  UnionAll

  A union of types over all values of a type parameter. UnionAll is used to describe
  parametric types where the values of some parameters are not known.

  Examples
  ≡≡≡≡≡≡≡≡≡≡

  julia> typeof(Vector)
  UnionAll
  
  julia> typeof(Vector{Int})
  DataType
1 Like

the concretetype and bitstype flags appear to be independent. I wonder if there is currently some type in the system that is bitstype but not concretetype?


@edit isbitstype(Tuple{Int,Int})
# isbitstype(@nospecialize t) = (@_total_meta; isa(t, DataType) && (t.flags & 0x8) == 0x8)

@edit isconcretetype(Vector{Int})

# isconcretetype(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && (t.flags & 0x2) == 0x2)

EDIT: I answer myself the definition requires that the type be of type DataType

julia> isa(Vector, DataType)
false

Still using the legth() function to distinguish cases, if the purpose is to define the names of the files to be generated, you could use a scheme like this

julia> function func(xs, args=nothing )
         # . . . here comes a lot of preparation to plot figures . . .
            s=length(xs)==1 ? "" : f"-p\%3.3d(i)"
               pre="myfig"
               suf=".png"
            for (i, x) in pairs(xs)
               # p = plot_a_figure_with_x(x, args )
               # savefig(p, "$pre$s$suf") # -> myfig-p001.png, myfig-p002.png, . . .
               println("$pre$s$suf", "    x = $x")
            end
       end
func (generic function with 2 methods)

julia> func(1:2:6) # iterable
myfig-p001.png    x = 1
myfig-p002.png    x = 3
myfig-p003.png    x = 5

julia> func(3) # scalar
myfig.png    x = 3

or using a flag

julia> function func(xs;scalar=false )
         # . . . here comes a lot of preparation to plot figures . . .
            s=scalar ? "" : f"-p\%3.3d(i)"
               pre="myfig"
               suf=".png"
            for (i, x) in pairs(xs)
               # p = plot_a_figure_with_x(x, args )
               # savefig(p, "$pre$s$suf") # -> myfig-p001.png, myfig-p002.png, . . .
               println("$pre$s$suf", "    x = $x")
            end
       end
func (generic function with 2 methods)

julia> func(1:2:6) # iterable
myfig-p001.png    x = 1
myfig-p002.png    x = 3
myfig-p003.png    x = 5

julia> func(3, scalar=true) # scalar
myfig.png    x = 3

Contrary to what you say, hasmethod will work perfectly since the goal is to have no error in the plotting program. It will not give an error if a “scalar” goes through the “iterable” part of the code. Also, I think in recent/future versions of julia the cost of hasmethod will become very small.

If you look at it from this perspective, then why bother to have a ‘scalar’ part of the code anyway or even check if it is iterable with hasmethod? If you pass a single number (a scalar), or a collection of numbers, both can be treated as ‘iterable’. The only reason for having these two parts is that you want the single number 1 to be treated differently from [1], and you will not be able to signal this with just Number types and hasmethod(iterate, ...).

Basically, my point is, hasmethod(iterate, ...) does not solve the semantic problem for types that implement iterate but are often seen as a scalar and you want to treat them as a scalar. The classic example is String, you often will want to treat it as a single scalar, but as it implements iterate it will be treated by the plotting method as a sequence of characters.

1 Like

You will get an error if you pass every object to the “iterable” part of the code. For example, a Symbol is not iterable. The goal is to have something working for every object, be it a collection or not.