Type-annotate vs convert


#1

Consider the issue of annotating types of objects taken from unknown sources. The following are two solutions to this problem:

function test1(a::Vector{Any})
       x = a[1]::Int
       2*x
end

and

function test2(a::Vector{Any})
       x = convert(Int, a[1])
       2*x
end

The manual recommends the first solution in the Performance-Tips section. It seems to me that the second solution might be preferable in many instances because the resulting code has greater genericity. But @code_warntype on test2 gave me many more warnings than on test1– it reported that x and 2*x in test2 were both of type Any. (Julia 0.5.2).

Why does the compiler not know the types in test2?


#2

convert is just a normal function — it doesn’t have any special sauce besides a very strong convention that convert(T, x) returns something of type T. Just like calling all other functions with an unknown argument, Julia doesn’t know exactly which method of convert will be called, and so it can’t make any assumptions about the return type.

The type assertion a[1]::T, on the other hand, will error if a[1] is anything but T, meaning that Julia can conclusively determine that every time control flow successfully passes the assertion a[1] is a T.


#3

Maybe both:

x = convert(Int, a[1])::Int   

#4

There have been discussions about enforcing that convert(T, x)::T, but for now you can use both convert and an assertion.


#5

Thanks for the explanation. In retrospect, this makes perfect sense.

As a general matter, I worry that Julia’s great flexibility (such as the flexibility to define a convert(T,x::X) that returns a non-T) opens up many pitfalls for the user. But this particular piece of flexibility seems potentially useful. So I have just submitted a PR for the manual to explain the point.


#6

Can you given an example for when convert(T, ...) returning a non-T would be useful?


#7

This is a bit of a stretch…

In the following old thread:

I proposed a solution (untested!) to the original poster’s problem. The solution involved a constructor for a type S that returned a non-S. I believe that this is permitted by the language. Meanwhile, there is a close relationship between constructors and convert. So I can imagine that there are situations for constructing complex types in which defining convert(T,x) to return a non-T might be useful.