Type instability if I map over array of functions


#1

Hello,

when I map over an array of functions, the ouput is type unstable.

Minimal example:

struct StructA
    val::Float64
end
struct StructB
    val::Float64
end
function init_add(struct_a::StructA)
    y -> struct_a.val + y + 0.1
end
function init_add(struct_b::StructB)
    y -> struct_b.val + y + 0.2
end
A = [init_add(StructA(1.1)), init_add(StructB(2.2))]
@code_warntype map(a -> a(0.1), A)

Output:

Variables:
  #self# <optimized out>
  f <optimized out>
  A::Array{Function,1}

Body:
  begin 
      return $(Expr(:invoke, MethodInstance for _collect(::Array{Function,1}, ::Base.Generator{Array{Function,1},##5#6}, ::Base.EltypeUnknown, ::Base.HasShape), :(Base._collect), :(A), :($(Expr(:new, Base.Generator{Array{Function,1},##5#6}, :($(QuoteNode(#5))), :(A)))), :($(QuoteNode(Base.EltypeUnknown()))), :($(QuoteNode(Base.HasShape())))))
  end::Array{_,1} where _

where Array{_,1} where _ is red.

This could easily be fixed by doing:

map(a -> a(0.1)::Float64, A)

but unfortunately I need to return Tuple with an anonymous function:

map(a -> (b -> a(b), a(0.1)), A)

Is there still a Trick to get this type stable?


#2

Function is an abstract type. Look at

julia> typeof(init_add(StructA(1.1)))
##3#4{StructA}

julia> typeof(init_add(StructB(2.2)))
##5#6{StructB}

Since these types are different, iterating over a Vector containing elements of both types is the same as iterating over any other type-heterogeneous collection (e.g. Number[1, 2.0]). See this post for example for some options.