Basic question about different type annotations in function declarations

I wanted to clarify my understanding of the following ways of defining a function.

  1. f(x) = …

  2. f(x::T) where {T} = …

  3. f(x::Any) = …

  • Is it true that 1. and 2. are equivalent if there are no type restrictions in the UnionAll? That is, the function is specialized for every different concrete input type.

  • Is it true that 3. is never specialized for a concrete type, but it is executed as if its argument is of type Any? I imagine this is important if one wants to limit excessive specializations for functions which do not benefit from it much.

There is no difference between those three, except that in 2. the type parameter T will be available inside the function body (you can anyway get that by calling T = typeof(x) later on.)

Each will be specialized every time. In order to avoid specialization, you can look at @nospecialize: https://docs.julialang.org/en/v1/base/base/#Base.@nospecialize

3 Likes

Yes that is true.

No that is not true. Julia will always specialize on the concrete type unless you use the @nospecialize macro.

1 Like

I see!

So this is different to the - in an abstract sense similar - struct declarations

  1. struct A
    a::Any
    end

  2. struct A{T}
    a::T
    end

Functions dealing with 1. won’t be able to specialize for a concrete field type, in contrast to 2. Correct?

There is a subtle difference that 2 will force specialization, whereas the other two leave it to the compiler. At least this is how it used to be Splatting arguments causes ~30x slow down.

4 Likes

Yes.