Conversion in typed globals (Julia 1.8)

I just want to understand this new feature in Julia 1.8

f(x::Int64) = x^2 + 1
f(3.0 + 0.0im) # ERROR: no method matching f(::ComplexF64), as expected
# But:
x::Int64 = 3.0 + 0.0im # no problem, conversion applied!

If I specify a global variable to be of a certain type, I was expecting that to be strict as with methods in functions, but surely I am missing something, any help just to understand will be appreciated.

In the Type declarations section of the manual, it says:

When appended to a variable on the left-hand side of an assignment, or as part of a local declaration, the :: operator […] declares the variable to always have the specified type, like a type declaration in a statically-typed language such as C. Every value assigned to the variable will be converted to the declared type using convert

For eg.,

 julia> let 
               local x::Int = 5.0
               @show x
x = 5

The same behaviour is extended to the global type declarations with 1.8, which seems reasonable and consistent.

(Btw, it seems this section of the manual needs an update, since it also says:

Currently, type declarations cannot be used in global scope

and doesn’t mention typed globals anywhere.)


This was an intentional change:

To be clear, it is still strict, in the sense that the conversion will only succeed if it’s actually possible to convert without loss of information:

julia> x::Int = 3 + 1im
ERROR: InexactError: Int64(3 + 1im)
 [1] Real
   @ ./complex.jl:44 [inlined]
 [2] convert(#unused#::Type{Int64}, x::Complex{Int64})
   @ Base ./number.jl:7
 [3] top-level scope
   @ REPL[2]:1

With functions, the strictness comes from method specialization i.e. Julia creates code specific for the particular types in the signature. converting arguments automatically would be incompatible with Julia’s dispatch paradigm.

Julia doesn’t intend to be a strictly typed language, so it enforces such strictness only when it has to.


Also, this type assertion is as strict as other assertions, e.g. like return type assertions:

julia> foo()::Int = rand(Int) + 0im
foo (generic function with 1 method)

julia> foo()

julia> foo()::Int = rand(Complex{Int})
foo (generic function with 1 method)

julia> foo()
ERROR: InexactError: Int64(4054552448232780990 + 7575752085304089316im)
 [1] Real
   @ ./complex.jl:44 [inlined]
 [2] convert
   @ ./number.jl:7 [inlined]
 [3] foo()
   @ Main ./REPL[3]:1
 [4] top-level scope
   @ REPL[4]:1

Type annotations in signatures are not type assertions - their use is for dispatch, never type conversions.