Hey Julianners,
I just cannot figure out how to make type stable code in case when we have a “sort of” heterogeneous array or tuple and we randomly pick an element.
I did this minimal example.
hn(qq) = begin
local r
q=qq[rand([1,3])]
if isa(q, Int)
r=4
elseif isa(q,Float32)
r=3f0
else
r=1e0
end
return r
end
@code_warntype hn((3,3f0,2))
hn((3,3f0,2))
Is this something that isn’t possible to resolve in any certain way in Julia?
Your function is fundamentally unstable, it literally draws a random type at runtime. Your type assertions only make individual branches type stable, but the return value of the function is still unknown to the compiler.
Yeah, you are right… I just want this to be type stable… 
I guess you basically proved it isn’t possible.
So:
fn(arr::heterogenous type) =
x=arr[pick random] # only available in run time
return x # cannot be deferred or fixed any certain way?
end
But then how to handle the heterogeneous types in Julia? 
In C++ we could write a very performant code with virtual functions I think. Does this something that cannot be accomplised with Julia?
You can use the trick with type assertions like you did, but inside the branches you should call a function that performs the work. You want to avoid the instability propagating to the compute heavy parts of your code.
When you have heterogeneous data whose elements are accessed based on runtime information, try to minimize the code that runs in the uninferred state, i.e. wrap subsequent code in a function that can be specialized to the different types. Using the profiler, you can then check that often the run-time dispatch overhead is negligible. That is, the fact that the top function call is type-unstable is not actually detrimental to performance.
So basically I could write something like this and it is optimal:
calc(a::Vector{Float32}) = a.+=1f0
calc(a::Vector{Float64}) = a.+=1e0
qn(hetarrs) = begin
arr = hetarrs[rand([1,2])]
calc(arr)
end
vv = (
randn(Float32,10000),
randn(Float64,10000),
)
# @code_warntype qn(vv)
qn(vv)
Yes, the advantage of doing it this way is that you pay the cost of the type instability a single time, at the call for calc.
This approach is called a “function barrier” where qn is unstable, but the calc function serves as a barrier for the instability beyond which the types are known. The call to calc incurs dynamic dispatch, but calc itself is not affected.