What does this line of code mean?

```
adds = add isa Union{AbstractArray, Tuple}? [add...] : [add]
```

Here ‘add’ is supposed to be a vector or a single number.

What does this line of code mean?

```
adds = add isa Union{AbstractArray, Tuple}? [add...] : [add]
```

Here ‘add’ is supposed to be a vector or a single number.

It makes an `adds`

array, regardless of whether `add`

is a scalar, array, or tuple. The `[add...]`

is to make an array (the brackets) consisting of the elements of `add`

, where `...`

means splat: list the elements as if not in an array. This is intended to work with either array or tuple. Finally, if `add`

is a scalar, the ternary else `[add]`

puts that into a 1-element array.

This code looks like the clunky stuff I would write. I imagine there are cleaner ways to do this.

2 Likes

I think the idiomatic way is to primarily write code for scalars, and broadcasting explicitly when using it on a vector. Or at least doing

```
toadds(adds::AbstractArray) = adds
toadds(adds::Tuple) = collect(adds)
toadds(add::T) where {T} = Vector{T}(add)
```

Splatting a potentially large vector is discouraged.

6 Likes

The expression `[a...]`

also works when `a`

is a scalar number

2 Likes

First, let’s see what that expression does.

```
julia> adds(add) = add isa Union{AbstractArray, Tuple} ? [add...] : [add]
adds (generic function with 1 method)
julia> adds(5)
1-element Vector{Int64}:
5
julia> adds([1,2])
2-element Vector{Int64}:
1
2
julia> adds(1:5)
5-element Vector{Int64}:
1
2
3
4
5
julia> adds((1,2,3))
3-element Vector{Int64}:
1
2
3
```

Now let’s try an alternate implementation.

```
julia> adds(add) = [add...]
adds (generic function with 1 method)
julia> adds(5)
1-element Vector{Int64}:
5
julia> adds([1,2])
2-element Vector{Int64}:
1
2
julia> adds(1:5)
5-element Vector{Int64}:
1
2
3
4
5
julia> adds((1,2,3))
3-element Vector{Int64}:
1
2
3
```

The second version works because numbers are also iterable in Julia. Iteration of a number just provides a number.

Let’s do some type analysis.

```
julia> @code_warntype adds(5)
MethodInstance for adds(::Int64)
from adds(add) @ Main REPL[15]:1
Arguments
#self#::Core.Const(adds)
add::Int64
Body::Vector{Int64}
1 ─ %1 = Core._apply_iterate(Base.iterate, Base.vect, add)::Vector{Int64}
└── return %1
julia> @code_warntype adds((1,2))
MethodInstance for adds(::Tuple{Int64, Int64})
from adds(add) @ Main REPL[15]:1
Arguments
#self#::Core.Const(adds)
add::Tuple{Int64, Int64}
Body::Vector{Int64}
1 ─ %1 = Core._apply_iterate(Base.iterate, Base.vect, add)::Vector{Int64}
└── return %1
julia> @code_warntype adds(1:5)
MethodInstance for adds(::UnitRange{Int64})
from adds(add) @ Main REPL[15]:1
Arguments
#self#::Core.Const(adds)
add::UnitRange{Int64}
Body::Union{Vector{Any}, Vector{Int64}}
1 ─ %1 = Core._apply_iterate(Base.iterate, Base.vect, add)::Union{Vector{Any}, Vector{Int64}}
└── return %1
julia> @code_warntype adds((1,2,3))
MethodInstance for adds(::Tuple{Int64, Int64, Int64})
from adds(add) @ Main REPL[15]:1
Arguments
#self#::Core.Const(adds)
add::Tuple{Int64, Int64, Int64}
Body::Vector{Int64}
1 ─ %1 = Core._apply_iterate(Base.iterate, Base.vect, add)::Vector{Int64}
```

We see there is some type instability in the case of a `UnitRange{Int}`

.

We can addess that.

```
julia> adds(add::AbstractVector{T}) where T = T[add...]
adds (generic function with 2 methods)
julia> @code_warntype adds(1:5)
MethodInstance for adds(::UnitRange{Int64})
from adds(add::AbstractVector{T}) where T @ Main REPL[26]:1
Static Parameters
T = Int64
Arguments
#self#::Core.Const(adds)
add::UnitRange{Int64}
Body::Vector{Int64}
1 ─ %1 = add::UnitRange{Int64}
│ %2 = Core.tuple($(Expr(:static_parameter, 1)))::Core.Const((Int64,))
│ %3 = Core._apply_iterate(Base.iterate, Base.getindex, %2, %1)::Vector{Int64}
```

In summary, the function seems to be able to take a an array, scalar number, or tuple and return a `Vector`

. The implementation can be simplified for numbers since they are iterable. If this function were to be applied to non-number scalars, it might not work. Type stability issues can be addressed by adding a type to the vector construction.

6 Likes

does not the function `adds`

do the same as `collect`

?

No, `collect`

does not always return a `Vector`

, since it keeps the “shape”:

```
julia> collect([1 2; 3 4])
2×2 Matrix{Int64}:
1 2
3 4
```

Returning to OP’s question, there are some good replies here. I was saying “like the clunky stuff I would write.” In Matlab, you often want a function to work equally well with scalars or arrays, so I suspect this code is ported from Matlab, as many of us have done in the past.

However, @gustaphe points out this can usually be avoided by broadcasting, which is also more idiomatic Julia. Broadcasting uses dot syntax, as in `f.(add)`

will apply function `f`

to `add`

whether its a scalar, tuple, or vector, and will also maintain the original “shape.” Where `f`

should just be written expecting a scalar.

The other alternatives discussed here are better than original. Thanks @mkitti for the type analysis! But they’re still just better ways to propagate the Matlab-ism, i.e. “putting lipstick on a pig.” Broadcasting is the preferred approach.