# Why would closure break type inference?

**URL:** <https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681>\
**Category:** General Usage\
**Created:** [March 7, 2020, 9:01am UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681 "2020-03-07T09:01:17Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![Mikhail\_Kagalenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikhail_kagalenko/32/13257_2.png) [@Mikhail\_Kagalenko](https://discourse.julialang.org/u/Mikhail_Kagalenko)\
**Post date:** [March 7, 2020, 9:01am UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/1 "2020-03-07T09:01:17Z")

</div>

```julia
function my_mean(A::AbstractArray)
  isempty(A) && return sum(A)/0
    n = length(A)
    x1 = first(A) / n
    _prom(x::T, y::S) where {T,S} = begin
      R = promote_type(T, S)
      return convert(R, x)
    end
    return sum(x->_prom(x,x1), A) / n
end

function my_mean_bad(A::AbstractArray)
    isempty(A) && return sum(A)/0
    n = length(A)
    x1 = first(A) / n
    let T = typeof(first(A)/n)
        return sum(x -> convert(promote_type(T, typeof(x)), x), A) / n 
    end
end

julia> @inferred(my_mean(Float32[]))
NaN32

julia> @inferred(my_mean_bad(Float32[]))
ERROR: return type Float32 does not match inferred return type Any
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] top-level scope at REPL[5]:1

```

---

<div class="post-metadata">

**Author:** ![waralex](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/waralex/32/13415_2.png) [@waralex](https://discourse.julialang.org/u/waralex)\
**Post date:** [March 7, 2020, 11:03am UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/2 "2020-03-07T11:03:57Z")

</div>

The problem is not with the closure, but in captured variable `T`. If you replace `T` with `typeof(first(A)/n)` then all be ok. I’m not a big specialist in Julia compiler, but I think that if `T` is a dynamic `DataType` then the compiler can’t be sure that it has a constant value in all calls of promote\_type, so at compilation time the result type of promote\_type(T,…) is `Any` and for `convert` its `Any` too

---

<div class="post-metadata">

**Author:** ![waralex](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/waralex/32/13415_2.png) [@waralex](https://discourse.julialang.org/u/waralex)\
**Post date:** [March 7, 2020, 11:19am UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/3 "2020-03-07T11:19:53Z")

</div>

Probably more accurately it sounds like this: at compilation time of the closure we only know the type of `T`, that is `DataType` but not its value. So we cannot recognize the result of `promote_type` and the result type of `convert` at compilation time

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [March 7, 2020, 12:41pm UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/4 "2020-03-07T12:41:23Z")

</div>

> [@Mikhail\_Kagalenko](#):
>
> ```julia
> function my_mean_bad(A::AbstractArray)
> isempty(A) && return sum(A)/0
> n = length(A)
> x1 = first(A) / n
> let T = typeof(first(A)/n)
> return sum(x -> convert(promote_type(T, typeof(x)), x), A) / n 
> end
> 
> ```

This is missing a terminating `end`. I think that you can just rewrite this as

```julia
function my_mean_bad(A::AbstractArray)
    isempty(A) && return sum(A)/0
    n = length(A)
    x1 = first(A) / n
    sum(x -> first(promote(x, x1)), A) / n
end

```

which infers fine.

---

<div class="post-metadata">

**Author:** ![Mikhail\_Kagalenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikhail_kagalenko/32/13257_2.png) [@Mikhail\_Kagalenko](https://discourse.julialang.org/u/Mikhail_Kagalenko)\
**Post date:** [March 7, 2020, 12:46pm UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/5 "2020-03-07T12:46:55Z")

</div>

That version does not work if an array has “missing” values.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [March 7, 2020, 12:55pm UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/6 "2020-03-07T12:55:01Z")

</div>

Your original question does not mention `missing` at all though.

The other function does not infer either for `missing`, eg

```julia
julia> @inferred(my_mean([missing 1f0]))
ERROR: return type Missing does not match inferred return type Any

```

What would you want to do for `missing` values, ignore them (see `skipmissing`), or return `missing` as the mean?

---

<div class="post-metadata">

**Author:** ![Mikhail\_Kagalenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikhail_kagalenko/32/13257_2.png) [@Mikhail\_Kagalenko](https://discourse.julialang.org/u/Mikhail_Kagalenko)\
**Post date:** [March 7, 2020, 1:01pm UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/7 "2020-03-07T13:01:52Z")

</div>

Yes, the my\_mean() version does not infer with the missing values as well, but it executes without an error, unlike the one you propose.

Edit: my\_mean() is similar to Base.sum() on “missing”

```julia
julia> @inferred(Base.sum([1,missing]))
ERROR: return type Missing does not match inferred return type Union{Missing, Int64}

```

---

<div class="post-metadata">

**Author:** ![waralex](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/waralex/32/13415_2.png) [@waralex](https://discourse.julialang.org/u/waralex)\
**Post date:** [March 7, 2020, 1:19pm UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/8 "2020-03-07T13:19:54Z")

</div>

```julia
function my_mean_bad(A::AbstractArray)
    isempty(A) && return sum(A)/0
    n = length(A)
    return sum(x -> convert(promote_type(typeof(first(A)/n), typeof(x)), x), A) / n 
end

```

infers fine and executes without an error with the missing values

---

<div class="post-metadata">

**Author:** ![Mikhail\_Kagalenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikhail_kagalenko/32/13257_2.png) [@Mikhail\_Kagalenko](https://discourse.julialang.org/u/Mikhail_Kagalenko)\
**Post date:** [March 7, 2020, 1:55pm UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/9 "2020-03-07T13:55:36Z")

</div>

The question isn’t how to fix the function, but why the type inference fails on the seemingly equivalent program.

---

<div class="post-metadata">

**Author:** ![Mikhail\_Kagalenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikhail_kagalenko/32/13257_2.png) [@Mikhail\_Kagalenko](https://discourse.julialang.org/u/Mikhail_Kagalenko)\
**Post date:** [March 7, 2020, 1:55pm UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/10 "2020-03-07T13:55:52Z")

</div>

> [@Tamas\_Papp](#):
>
> This is missing a terminating `end` . I think that you can just rewrite this as

Fixed, thanks

---

<div class="post-metadata">

**Author:** ![Mikhail\_Kagalenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikhail_kagalenko/32/13257_2.png) [@Mikhail\_Kagalenko](https://discourse.julialang.org/u/Mikhail_Kagalenko)\
**Post date:** [March 7, 2020, 2:00pm UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/11 "2020-03-07T14:00:20Z")

</div>

> [@waralex](#):
>
> think that if `T` is a dynamic `DataType` then the compiler can’t be sure that it has a constant value in all calls of promote\_type,

This version also breaks the inference

```julia
function my_mean_bad(A::AbstractArray)
   isempty(A) && return sum(A)/0
   n = length(A)
   x1 = first(A) / n
   T = typeof(first(A)/n)
   return sum(x -> convert(promote_type(T, typeof(x)), x), A) / n
end

```

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [March 7, 2020, 2:12pm UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/12 "2020-03-07T14:12:28Z")

</div>

I think you need to specialize on the type. This dummy function barrier works:

```julia
function my_mean_bad(A::AbstractArray)
    isempty(A) && return sum(A)/0
    n = length(A)
    x1 = first(A) / n
    _core(::Type{T}) where {T} = sum(x -> convert(promote_type(T, typeof(x)), x), A) / n
    _core(typeof(x1))
end

```

Similarly `_core(x1::T) where T` etc.

---

<div class="post-metadata">

**Author:** ![Mikhail\_Kagalenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikhail_kagalenko/32/13257_2.png) [@Mikhail\_Kagalenko](https://discourse.julialang.org/u/Mikhail_Kagalenko)\
**Post date:** [March 7, 2020, 2:33pm UTC](https://discourse.julialang.org/t/why-would-closure-break-type-inference/35681/13 "2020-03-07T14:33:58Z")

</div>

Yes, that’s another version that works. Probably, a manifestation of [this issue](https://github.com/JuliaLang/julia/issues/23618)
