# Type instability when using map with closures

**URL:** <https://discourse.julialang.org/t/type-instability-when-using-map-with-closures/101510>\
**Category:** General Usage\
**Tags:** type-stability, closure, map\
**Created:** [July 12, 2023, 7:39am UTC](https://discourse.julialang.org/t/type-instability-when-using-map-with-closures/101510 "2023-07-12T07:39:14Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![ynue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ynue/32/44040_2.png) [@ynue](https://discourse.julialang.org/u/ynue)\
**Post date:** [July 12, 2023, 7:39am UTC](https://discourse.julialang.org/t/type-instability-when-using-map-with-closures/101510/1 "2023-07-12T07:39:14Z")

</div>

I have observed a surprising behaviour of `map` with closures. It seems that type instabilities appear when the function called by `map` overwrites a variable that belongs to an outer scope (only changing its value while keeping the same type). Here is a MWE:

```julia
function map_bad(x)
    r = rand(typeof(first(x)))
    c = r
    map(x) do xi
        r = rand(typeof(xi))
        xi + c + r
    end
end

function map_good(x)
    r = rand(typeof(first(x)))
    c = r
    map(x) do xi
        ri = rand(typeof(xi))
        xi + c + ri
    end
end

using BenchmarkTools
x = rand(10)
@btime map_bad($x); # 3.193 μs (44 allocations: 1.00 KiB)
@btime map_good($x); # 40.237 ns (1 allocation: 144 bytes)

```

I am quite surprised by the difference in execution time and memory allocations. I inspected both functions with `@code_warntype`:

```julia
MethodInstance for map_bad(::Vector{Float64})
  from map_bad(x) @ Main REPL[2]:1
Arguments
  #self#::Core.Const(map_bad)
  x::Vector{Float64}
Locals
  #5::var"#5#6"
  c::Any
  r@_5::Core.Box
  r@_6::Union{}
Body::Vector
1 ─ Core.NewvarNode(:(#5))
│ Core.NewvarNode(:(c))
│ (r@_5 = Core.Box())
│ %4 = Main.first(x)::Float64
│ %5 = Main.typeof(%4)::Core.Const(Float64)
│ %6 = Main.rand(%5)::Float64
│ Core.setfield!(r@_5, :contents, %6)
│ %8 = Core.isdefined(r@_5, :contents)::Bool
└── goto #3 if not %8
2 ─ goto #4
3 ─ Core.NewvarNode(:(r@_6))
└── r@_6
4 ┄ (c = Core.getfield(r@_5, :contents))
│ %14 = Main.:(var"#5#6")::Core.Const(var"#5#6")
│ %15 = Core.typeof(c)::DataType
│ %16 = Core.apply_type(%14, %15)::Type{var"#5#6"{_A}} where _A
│ %17 = c::Any
│ (#5 = %new(%16, %17, r@_5))
│ %19 = #5::var"#5#6"
│ %20 = Main.map(%19, x)::Vector
└── return %20

```

```julia
MethodInstance for map_good(::Vector{Float64})
  from map_good(x) @ Main REPL[3]:1
Arguments
  #self#::Core.Const(map_good)
  x::Vector{Float64}
Locals
  #7::var"#7#8"{Float64}
  c::Float64
  r::Float64
Body::Vector{Float64}
1 ─ %1 = Main.first(x)::Float64
│ %2 = Main.typeof(%1)::Core.Const(Float64)
│ (r = Main.rand(%2))
│ (c = r)
│ %5 = Main.:(var"#7#8")::Core.Const(var"#7#8")
│ %6 = Core.typeof(c)::Core.Const(Float64)
│ %7 = Core.apply_type(%5, %6)::Core.Const(var"#7#8"{Float64})
│ (#7 = %new(%7, c))
│ %9 = #7::var"#7#8"{Float64}
│ %10 = Main.map(%9, x)::Vector{Float64}
└── return %10

```

In the case of `map_bad`, I do not understand why `r` is created this way. I am also surprised that the type of `c` cannot be inferred at compile time. Does anyone have an idea of what is happening?

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [July 12, 2023, 7:47am UTC](https://discourse.julialang.org/t/type-instability-when-using-map-with-closures/101510/2 "2023-07-12T07:47:54Z")

</div>

[https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-captured](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-captured)

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [July 12, 2023, 7:49am UTC](https://discourse.julialang.org/t/type-instability-when-using-map-with-closures/101510/3 "2023-07-12T07:49:58Z")

</div>

You can also read [The Great Debate](https://discourse.julialang.org/t/rfc-some-ideas-to-tackle-15276-performance-of-captured-variables-in-closures/95260) if you want to get into the weeds!

---

<div class="post-metadata">

**Author:** ![ynue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ynue/32/44040_2.png) [@ynue](https://discourse.julialang.org/u/ynue)\
**Post date:** [July 12, 2023, 9:54am UTC](https://discourse.julialang.org/t/type-instability-when-using-map-with-closures/101510/4 "2023-07-12T09:54:37Z")

</div>

Thank you very much for pointing this out, I will definitely keep an eye on this thread!
