# What on earth does Any[...] do?

I’ve been using Julia nearly daily for about a year and thought I was fluent in the basics by now. Here’s an example that proves I was mistaken.

(I believe that) The syntax `T[]` defines an array literal and casts the elements to type `T`. For example:

``````julia> Float64[1,2,3]
3-element Array{Float64,1}:
1.0
2.0
3.0
``````

Similarly, (I believe that) `Any[]` is useful for initializing an array with elements of type `Any`, so that it can hold elements of other types later.

``````julia> Any[1,2,3]
3-element Array{Any,1}:
1
2
3
``````

So far so good. Now let’s do the same thing on a more complex array:

``````julia> x = Any[ [1,2,3], ["1","2","3"], [1.0,2.0,3.0] ]
3-element Array{Any,1}:
[1, 2, 3]
String["1", "2", "3"]
[1.0, 2.0, 3.0]

julia> typeof.(x)
3-element Array{DataType,1}:
Array{Int64,1}
Array{String,1}
Array{Float64,1}
``````

Instead of casting the elements of `x` to Any, it seems to have done the complete opposite and made the types more specific. On a heterogeneous array, I would have expected `Any[]` to produce output similar to what you get if you leave out the `Any` specification, like this:

``````julia> y = [ [1,2,3], ["1","2","3"], [1.0,2.0,3.0] ]
3-element Array{Array{Any,1},1}:
Any[1, 2, 3]
Any["1", "2", "3"]
Any[1.0, 2.0, 3.0]
``````

What in the world is going on here? What is this called, and where is it documented? (Googling “Julia Any” isn’t very helpful …)

``````julia> typeof(convert(Any, 1))
Int64

julia> typeof(convert(Any, "foo"))
String
``````

in fact

``````convert(::Type{Any}, x) = x
``````
1 Like

`Any` is an abstract type, so you can’t have concrete instantiations of it. However, containers can have `Any` as their element type. When this is true it is required that the elements of the array have types `<: Any`, but since `Any` is an abstract type (and all concrete types are subtypes of it), `convert(Any, x) = x`, \forall x.

When you do `x = Any[ [1,2,3], ["1","2","3"], [1.0,2.0,3.0]]` all you are doing is declaring that your Array should have `eltype` `Any`. Since `Vector{Int64} <: Any`, `Vector{String} <: Any` and `Vector{Float64} <: Any`, this constructor is perfectly valid and the conversions that take place are trivial.

It’s a little less clear to me why the result is different when you omit the prefixed `Any`, but it’s just a result of how the compiler processes literals.

2 Likes

I think the correct question is what does `[[x1, y1, z1], [x2, y2, z2], [x3, y3, z3]]` do instead of `Any[...]` do.
`[[x1, y1, z1], [x2, y2, z2], [x3, y3, z3]]` will try to cast all the elements into a common type, and in your example, the common type if `Any`, so it is the behaviour of

``````julia> y = [ [1,2,3], ["1","2","3"], [1.0,2.0,3.0] ]
3-element Array{Array{Any,1},1}:
Any[1, 2, 3]
Any["1", "2", "3"]
Any[1.0, 2.0, 3.0]
``````
1 Like

Interesting! This is a bit subtle, but it’s a natural result of arrays attempting to promote their contents to a common type.

``````julia> Any[ [1,2,3], ["1","2","3"], [1.0,2.0,3.0] ]
3-element Array{Any,1}:
[1, 2, 3]
String["1", "2", "3"]
[1.0, 2.0, 3.0]

julia> [ [1,2,3], ["1","2","3"], [1.0,2.0,3.0] ]
3-element Array{Array{Any,1},1}:
Any[1, 2, 3]
Any["1", "2", "3"]
Any[1.0, 2.0, 3.0]
``````

In the first case, the outer array has explicit element type `Any` so its elements are taken as is when constructing the array. In the second case, the outer array has no explicit element type so `typejoin` is called to try to find a “reasonable” common element type – which is `Vector{Any}` since all of the things passed to it are vectors but they don’t have a common element type. So they’re all converted to the type `Vector{Any}` before construction.

The thing that’s questionable here is whether arrays should recursively promote to a common type like this when it ends up “pessimizing” their individual element types so much. The motivation for the array promotion rule is (more common) cases like this:

``````julia> [ [1, 2, 3], [1, 0.5, 0.25, 0.125], [-1, -2] ]
3-element Array{Array{Float64,1},1}:
[1.0, 2.0, 3.0]
[1.0, 0.5, 0.25, 0.125]
[-1.0, -2.0]
``````

Here’s it’s much better in terms of types and performance to convert all of the internal arrays to a single common concrete element type. I’ve filed an issue about this: #24988 since it’s worth considering changing the array promotion rule here.

3 Likes

BTW, `T[]` will only cast the elements to type `T`. In the case of vector of vectors, you need to do something like `Vector{T}[]` to convert the elements in the inner vectors, for example, `Vector{Any}[ [1,2,3], ["1","2","3"], [1.0,2.0,3.0] ]` will cast all the elements in the inner vectors to type `Any`.

1 Like

`convert(::Type{Any}, x) = x`
syntax?

Specifically the `::Type{Any}` part. How is that understood by `convert` in this example?

Does `::Type{Any}` refer to the type itself? Or an instance of the type?

I see that I can do

``````function f1(::Array{Int64})
return true # returns true as long as the argument is <: Array{Int64}, I think?
end
``````

but not

``````function f2(Array{Int64})
``````

which is a syntax error.

I guess I could use `::Type{Any}` to test that whatever is passed to `f1` is an instance of the type. Are there other uses for this syntax? Is there some syntax that would let me do:

``````function f3(::Array{Int64})
return size( the argument ) # but how do I refer to that in this scope?
end
``````

I see this syntax around sometimes but I don’t think I fully get it.

For any Julia type `T`, we have `T::Type{T}`. This lets you write functions that dispatch on a particular type passed in as an argument:

``````julia> f(::Type{<:AbstractFloat}) = "a type which is float-like"
f (generic function with 2 methods)

julia> f(::Type{Int}) = "the type Int"
f (generic function with 3 methods)

julia> f(::Type{String}) = "the type String"
f (generic function with 4 methods)

julia> f(Float64)
"a type which is float-like"

julia> f(Int)
"the type Int"

julia> f(String)
"the type String"
``````

this should actually look weird, because it’s kind of like we’re dispatching on the value of the argument rather than its type. After all, `Int, Float64` and `String` are all just values of type `DataType`:

``````julia> typeof(Int)
DataType
``````

But because dispatching on types as function arguments is so useful, the `Type` construct is specifically designed to make that possible. This leads to a particular weirdness:

``````julia> Int::Type{Int}
Int64

julia> Int isa Type{Int}
true

julia> typeof(Int)
DataType

julia> typeof(Int) == Type{Int}
false
``````

fortunately, that’s the only weirdness. Otherwise, `Type` arguments are just like any other function argument; they just look weird because they’re often written as `f(::Type{T})` with the variable name omitted rather than `f(x::Type{T})`. Including the variable name makes the behavior more clear:

``````julia> function f(x::Type{T}) where T
@show x
@show T
@assert x == T
end
f (generic function with 1 method)

julia> f(Int)
x = Int64
T = Int64

julia> f(Real)
x = Real
T = Real

julia> f(Float64)
x = Float64
T = Float64
``````

You can see in this case why the variable name `x` is typically omitted, because its value (the type that was passed to `f`) is already captured in the variable `T`. So we would generally write `f` as:

``````julia> function f(::Type{T}) where T
@show T
end
f (generic function with 1 method)

julia> f(Int)
T = Int64
Int64

julia> f(Real)
T = Real
Real
``````

and refer to the type as `T` inside the function.

The most common place to see `Type{T}` is in `convert()` methods:

``````convert(::Type{MySpecialType}, x::Int) = make_my_special_type_from_int(x)
convert(::Type{Int}, y::MySpecialType) = make_int_from_my_special_type(y)
``````

since `convert` is called with the destination type as its first argument:

``````y = convert(MySpecialType, 1)
convert(Int, y)
``````

`function f3(::Array{Int64})`
` return size( the argument ) # but how do I refer to that in this scope?`
`end`

dispatching with `Type{T}` is useful when you expect a type as an argument, not an instance of that type. So your `f3` would just be:

``````function f3(x::Array{Int64})
return size(x)
end
``````

just like any other method in Julia.

8 Likes

Update: so, I ran those examples in a 0.6 session without realizing it. This is already changed/fixed on master:

``````julia> VERSION
v"0.7.0-DEV.2976"

julia> [ [1, 2, 3], ["1", "2", "3"], [1.0, 2.0, 3.0] ]
3-element Array{Array{T,1} where T,1}:
[1, 2, 3]
["1", "2", "3"]
[1.0, 2.0, 3.0]
``````
1 Like