Why is type inference failing here?

In the following example, it seems like it should be simple for Julia to infer that the type of prev_x is Float64. Why is it instead getting assigned a boxed type?

julia> function tf(xs)
        prev_x = xs[1]
        map(xs) do x
         r = x / prev_x
         prev_x = x
         r
        end
       end
tf (generic function with 1 method)

julia> xs = rand(10^6);

julia> @time tf(xs);
  0.178356 seconds (3.12 M allocations: 61.497 MiB, 67.31% compilation time)

julia> @time tf(xs);
  0.060390 seconds (3.00 M allocations: 53.406 MiB)

julia> @code_warntype tf(xs)
MethodInstance for tf(::Vector{Float64})
  from tf(xs) @ Main REPL[243]:1
Arguments
  #self#::Core.Const(tf)
  xs::Vector{Float64}
Locals
  #228::var"#228#229"
  prev_x::Core.Box
Body::Any
1 ─      (prev_x = Core.Box())
│   %2 = Base.getindex(xs, 1)::Float64
│        Core.setfield!(prev_x, :contents, %2)
│        (#228 = %new(Main.:(var"#228#229"), prev_x))
│   %5 = #228::var"#228#229"
│   %6 = Main.map(%5, xs)::Any
└──      return %6

I think the closure inside map is preventing the compiler from accessing type information of the outer variable.

This is a known issue, and there was a discussion some weeks ago about syntax that would allow this kind of information to be handled down.

I am writing from my phone and cannot test this, but try boxing it yourself in a Ref, or to manually annotate type information in closure’s variable like:

... do x::eltype(xs)
...

Thanks, but looks like that’s currently not allowed:

julia> function tf(xs)
        prev_x = xs[1]
        map(xs) do x::eltype(xs)
         r = x/prev_x
         prev_x = x
         r
        end
       end
ERROR: syntax: local variable xs cannot be used in closure declaration
Stacktrace:
 [1] top-level scope
   @ REPL[337]:1

I also tried x::Float64, but that didn’t help with the type inference.

Make prev_x a Ref or use a loop instead.
I think the loop is cleaner.