Specifying that a function which can take a vector or scalar as input

I frequently find myself writing functions which are fundamentally not that different for scalars or vectors. Take this for example:

julia> function silly_print(x)
    for i = 1:length(x)
        println(x[i])
    end
end

julia> silly_print(2)
2

julia> silly_print([2,4])
2
4

Specifying that x is a Vector breaks this behaviour:

julia> function silly_print_vector(x::Vector)
    for i = 1:length(x)
        println(x[i])
    end
end
silly_print_vector (generic function with 1 method)

julia> silly_print_vector(2)
ERROR: MethodError: no method matching silly_print_vector(::Int64)
Closest candidates are:
  silly_print_vector(::Array{T,1} where T) at none:1
Stacktrace:
 [1] top-level scope at none:0

Is there a workaround for this, without writing two identical functions but for different types?

You can write it just for scalars and then use broadcasting. E.g.,

julia> println.(1:3);
1
2
3
4 Likes

Hadn’t thought of that!

If you need more complex things than one ., it often works well to define say silly_print(x::Number), and then silly_print(x::AbstractArray) which does some work (say it prints summary(x)) before iself calling the ::Number method.

2 Likes

If you really need a function that accepts both arrays and scalars, either don’t declare the argument type at all (there is no performance penalty in Julia for duck typing!) just use a Union: declare function myfunction(x::Union{Number, AbstractVector}, for example.

But if your function is that generic, you way want it to work for any iterable object anyway, in which case it is better to duck type and avoid indexing:

function myfunction(itr)
    for x in itr
        # do something with x
    end
end
4 Likes