# Type instability seemingly due to redundant inner functions that are never called

**URL:** <https://discourse.julialang.org/t/type-instability-seemingly-due-to-redundant-inner-functions-that-are-never-called/133227>\
**Category:** General Usage\
**Tags:** question, type-stability, closure, anonymous-function\
**Created:** [October 17, 2025, 7:03am UTC](https://discourse.julialang.org/t/type-instability-seemingly-due-to-redundant-inner-functions-that-are-never-called/133227 "2025-10-17T07:03:17Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Nikos\_Gianniotis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nikos_gianniotis/32/11487_2.png) [@Nikos\_Gianniotis](https://discourse.julialang.org/u/Nikos_Gianniotis)\
**Post date:** [October 17, 2025, 7:03am UTC](https://discourse.julialang.org/t/type-instability-seemingly-due-to-redundant-inner-functions-that-are-never-called/133227/1 "2025-10-17T07:03:17Z")

</div>

I have created an MWE that can readily executed by copy-pasting to the REPL, provided package DispatchDoctor.jl is installed.

What follows below is:

- A variable `Z` is created in global scope (see end of script below).

- A function `mwe` that, according to DispatchDoctor.jl, exhibits type instability when called with `mwe(Z)`. The instability comes from an inner function called `lowerbound`. Function `lowerbound` defines in its body two anonymous¹ functions that are never called.

- A function `mwe_2` that is identical to `mwe`, but where the first anonymous function that is never called is now commented out. When called with `mwe_2(Z)`, no type instability is detected.

- A function `mwe_3` that is identical to `mwe`, but where the second anonymous function that is never called is now commented out. When called with `mwe_3(Z)`, no type instability is detected.

* * *

My question: why do the two redundant anonymous function influence the type instability?

* * *

¹ As pointed out below by @nsajko, these are not actually anonymous functions. I point out the mistake here, but do not correct it above so that the comment below does not appear out of place.

* * *

```julia-auto
using DispatchDoctor

function mwe(Z)

    local kernel(xᵢ, xⱼ, σf, ℓ) = σf * exp( - abs2(xᵢ - xⱼ) * ℓ)

    @stable function lowerbound(σf, ℓ)

        local k(x, z) = kernel(x, z, σf, ℓ) # this is never called

        local k(x) = kernel.(x, Z, σf, ℓ)

        local s(x) = dot(k(x), k(x)) # this is never called

        return k(1.0)

    end

    lowerbound(1.0, 1.0)

end

# same as mwe but line "local s(x) = dot(k(x), k(x))" is commented out
function mwe_2(Z) 

    local kernel(xᵢ, xⱼ, σf, ℓ) = σf * exp( - abs2(xᵢ - xⱼ) * ℓ)

   @stable function lowerbound(σf, ℓ)

        local k(x, z) = kernel(x, z, σf, ℓ) # this is never called

        local k(x) = kernel.(x, Z, σf, ℓ)

        # local s(x) = dot(k(x), k(x)) # this is never called

        return k(1.0)

    end

    lowerbound(1.0, 1.0)

end

# same as mwe but line "local k(x, z) = kernel(x, z, σf, ℓ)" is commented out
function mwe_3(Z)

    local kernel(xᵢ, xⱼ, σf, ℓ) = σf * exp( - abs2(xᵢ - xⱼ) * ℓ)

    @stable function lowerbound(σf, ℓ)

        # local k(x, z) = kernel(x, z, σf, ℓ) # this is never called

        local k(x) = kernel.(x, Z, σf, ℓ)

        local s(x) = dot(k(x), k(x)) # this is never called

        return k(1.0)

    end

    lowerbound(1.0, 1.0)

end

# I am aware that this is a *non-const* global
Z = collect(LinRange(0, 30, 30));

mwe(Z) # type instability detected
mwe_2(Z) # NO type instability detected
mwe_3(Z) # NO type instability detected

```

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [October 17, 2025, 11:43am UTC](https://discourse.julialang.org/t/type-instability-seemingly-due-to-redundant-inner-functions-that-are-never-called/133227/2 "2025-10-17T11:43:23Z")

</div>

A rough answer is that closures are currently implemented at an early phase of compilation, called lowering, and lowering runs before inference and optimization. So lowering is mostly just based on syntax. I don’t understand the specific reason here, but basically lowering gives up here and boxes `k`, causing bad type inference for the later compilation phases (`k` is inferred as `Any`, the least precise type).

Seems curious that this happens even though `k` is never (re)assigned.

Some links:

- [performance of captured variables in closures · Issue #15276 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/15276)

- [State of closures, Fix1/Fix2](https://discourse.julialang.org/t/state-of-closures-fix1-fix2/118997)

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [October 17, 2025, 11:51am UTC](https://discourse.julialang.org/t/type-instability-seemingly-due-to-redundant-inner-functions-that-are-never-called/133227/3 "2025-10-17T11:51:44Z")

</div>

A possible workaround seems to be to define the two methods of `k` together in a `let`:

```julia-repl
julia> using Cthulhu: @descend

julia> kernel(xᵢ, xⱼ, σf, ℓ) = σf * exp( - abs2(xᵢ - xⱼ) * ℓ)
kernel (generic function with 1 method)

julia> function mwe(Z)
           function lowerbound(σf, ℓ)
               k = let k(x, z) = kernel(x, z, σf, ℓ)
                   k(x) = kernel.(x, Z, σf, ℓ)
                   k
               end
               s(x) = dot(k(x), k(x))
               k(1.0)
           end
           lowerbound(1.0, 1.0)
       end
mwe (generic function with 1 method)

julia> Z = collect(LinRange(0, 30, 30));

julia> @descend remarks=true with_effects=true optimize=false mwe(Z) # infers fine

```

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [October 17, 2025, 12:07pm UTC](https://discourse.julialang.org/t/type-instability-seemingly-due-to-redundant-inner-functions-that-are-never-called/133227/4 "2025-10-17T12:07:13Z")

</div>

> [@Nikos\_Gianniotis](#):
>
> anonymous function

BTW, off-topic comment, just regarding nomenclature: `s` and `k` both have names (“s” and “k”, respectively), so neither is an anonymous function. Example of an anonymous function: `x -> 3 * x`.

---

<div class="post-metadata">

**Author:** ![Nikos\_Gianniotis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nikos_gianniotis/32/11487_2.png) [@Nikos\_Gianniotis](https://discourse.julialang.org/u/Nikos_Gianniotis)\
**Post date:** [October 18, 2025, 5:26pm UTC](https://discourse.julialang.org/t/type-instability-seemingly-due-to-redundant-inner-functions-that-are-never-called/133227/5 "2025-10-18T17:26:32Z")

</div>

Thanks for your answer. The links referred me to material that is too advanced for me, but helped me nevertheless understand a bit more the nature of the problem. I am surprised that a seemingly innocuous redundant code would lead to type instability. Once I corrected this issue in my actual code, I achieved a speed up of about 75-100%. I think I will start avoiding closures for now.

---

<div class="post-metadata">

**Author:** ![Nikos\_Gianniotis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nikos_gianniotis/32/11487_2.png) [@Nikos\_Gianniotis](https://discourse.julialang.org/u/Nikos_Gianniotis)\
**Post date:** [October 18, 2025, 5:27pm UTC](https://discourse.julialang.org/t/type-instability-seemingly-due-to-redundant-inner-functions-that-are-never-called/133227/6 "2025-10-18T17:27:26Z")

</div>

> [@nsajko](#):
>
> BTW, off-topic comment, just regarding nomenclature: `s` and `k` both have names (“s” and “k”, respectively), so neither is an anonymous function. Example of an anonymous function: `x -> 3 * x`.

Thanks for taking the time to point this out, I was very sloppy indeed.
