Anonymous functions weird behaviour with Array{Arrays}

question

#1

I waned to ask you about a weird thing that happens to me when I use anonymous functions with Array{Array}:

L = x -> [x[1:4],reshape(x[5:8],2,2)] #my anonymous functions generates an Array{Array{Float64,N},1}
L(randn(8))
2-element Array{Array{Float64,N},1}:
 [-0.557195,-1.62936,-0.340156,-0.947051]
 [2.19154 0.160976; 1.1349 0.201494]

then if I sum this directly with an Array{Array{Float64,N},1} I should also get an Array{Array{Float64,N},1}!
but this is what happens:

L(randn(8))+[randn(4),randn(2,2)]
2-element Array{Array{T,N},1}:
 [0.703503,-1.38808,-0.641571,1.70502]
 [-1.86622 -1.91455; 2.22344 0.450923]

but this actually gives an Array{Array{T,N},1}. Isn’t this weird? Or am I doing something wrong?


#2

This isn’t related to anonymous functions, as it can be reproduced like this:

julia> a = [[1], [2 3]]
2-element Array{Array{Int64,N},1}:
 [1]
 [2 3]

julia> a + a
2-element Array{Array{T,N},1}:
 [2]
 [4 6]

Compare the types in above code with post #6.


#3

It is generally not a good idea to mix objects of different types in an array. What are you trying to do? You could try using a tuple instead.


#4

No, you’re not doing anything wrong. These types are determined by inference, and inference is an imperfect approximation. There’s no limit to how hard inference could try to get types as exact as possible — and indeed it will never be able to figure out some cases (see: https://en.wikipedia.org/wiki/Halting_problem). So at some point it must give up and return an approximation. The only question is where to draw that line.

In my experience, once inference can no longer concretely figure out one of the parameters (N in this case), it doesn’t “try” as hard to figure out the other parameters. If, instead, you make sure that all the inner arrays are two-dimensional, it’s able to determine the types exactly.


#5

well tuples are not really attractive as they are sometimes difficult to deal with. say I want to sum two of them I cannot simply write a+b. Is it that wrong to use Array{Array} with different sizes? Thank you!


#6

Different sizes per se is not the problem, different number of dimensions is. Compare e.g.

julia> a=[hcat([1]), [2 3]]
2-element Array{Array{Int64,2},1}:
 [1]  
 [2 3]

julia> a+a
2-element Array{Array{Int64,2},1}:
 [2]  
 [4 6]

#7

This I understand, but why is it T instead of Any? I find this confusing.


#8

The T in Vector{T} represents a TypeVar wildcard — it can indeed match any type. It might be a Vector{Any}, but it could also be a Vector{Int} or Vector{Real}. This may get more obvious in the future as there are changes planned to how this prints. It might eventually be printed as Vector{T} where T <: Any.

This is different from Any in Vector{Any} — here Julia actually knows more about the type. It knows that it will definitely be a heterogenous array that can contain any object.

This is particularly significant because Julia’s type parameters are invariant.


#9

I’m not sure what changed, but this T output only happens on 0.5. On a recent (~ 2 days) trunk build I get:

julia> L = x -> [x[1:4],reshape(x[5:8],2,2)]
(::#1) (generic function with 1 method)

julia> L(randn(8))+[randn(4),randn(2,2)]
2-element Array{Array{Float64,N},1}:
 [0.472211,0.0546804,0.613357,0.422507]
 [2.78885 -0.646303; -0.120245 -0.950541]

(and 2-element Array{Array{Int64,N},1} for #4)