# Need help with incomprehensible typeinference

**URL:** <https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056>\
**Category:** Performance\
**Tags:** question\
**Created:** [May 14, 2022, 11:15am UTC](https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056 "2022-05-14T11:15:25Z")\
**Posts on this page:** 1\
**Showing post:** 6

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [May 14, 2022, 1:14pm UTC](https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056/6 "2022-05-14T13:14:13Z")

</div>

> **Here's a full REPL session. where inference seems to break, but maybe I'm missing something with globals and softscope.**
>
> ```julia
> julia> using ExtensibleEffects
> 
> julia> using TypeClasses
> 
> julia> using Test
> 
> julia>
> 
> julia> vector_of_eff_of_vector = map(x -> noeffect([x]), [1, 20])
> 2-element Vector{ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}}:
> Eff(effectful=NoEffect{Vector{Int64}}([1]), length(cont)=0)
> Eff(effectful=NoEffect{Vector{Int64}}([20]), length(cont)=0)
> 
> julia> e1 = vector_of_eff_of_vector[1]
> Eff(effectful=NoEffect{Vector{Int64}}([1]), length(cont)=0)
> 
> julia> e2 = vector_of_eff_of_vector[2]
> Eff(effectful=NoEffect{Vector{Int64}}([20]), length(cont)=0)
> 
> julia> function prepare_test(e1, e2)
> combine(v1, v2) = [v1; v2]
> curried_combine(v1) = v2 -> combine(v1, v2)
> 
> e1_f = map(curried_combine, e1)
> f_flatmap(f) = TypeClasses.map(v2 -> f(v2), e2)
> f_flatmap, e1_f
> end
> prepare_test (generic function with 1 method)
> 
> julia>
> 
> julia> f_flatmap, e1_f = prepare_test(e1, e2)
> (f_flatmap, Eff(effectful=NoEffect{var"#3#7"{Vector{Int64}, var"#combine#5"}}(var"#3#7"{Vector{Int64}, var"#combine#5"}([1], var"#combine#5"())), length(cont)=0))
> 
> julia> @inferred TypeClasses.flatmap(f_flatmap, e1_f) # infers perfectly
> Eff(effectful=NoEffect{Vector{Int64}}([1, 20]), length(cont)=0)
> 
> julia> function test_infers(e1, e2)
> f_flatmap, e1_f = prepare_test(e1, e2)
> TypeClasses.flatmap(f_flatmap, e1_f)
> end
> test_infers (generic function with 1 method)
> 
> julia>
> 
> julia> @inferred test_infers(e1, e2) # infers perfectly
> Eff(effectful=NoEffect{Vector{Int64}}([1, 20]), length(cont)=0)
> 
> julia> combine(v1, v2) = [v1; v2]
> ERROR: error in method definition: function TypeClasses.combine must be explicitly imported to be extended
> Stacktrace:
> [1] top-level scope
> @ none:0
> [2] top-level scope
> @ REPL[14]:1
> 
> julia> curried_combine(v1) = v2 -> combine(v1, v2)
> curried_combine (generic function with 1 method)
> 
> julia> e1_f = map(curried_combine, e1)
> Eff(effectful=NoEffect{var"#10#11"{Vector{Int64}}}(var"#10#11"{Vector{Int64}}([1])), length(cont)=0)
> 
> julia> f_flatmap(f) = TypeClasses.map(v2 -> f(v2), e2)
> ERROR: cannot define function f_flatmap; it already has a value
> Stacktrace:
> [1] top-level scope
> @ none:0
> [2] top-level scope
> @ REPL[17]:1
> 
> julia> f_flatmap, e1_f
> (f_flatmap, Eff(effectful=NoEffect{var"#10#11"{Vector{Int64}}}(var"#10#11"{Vector{Int64}}([1])), length(cont)=0))
> 
> julia> function prepare_test(e1, e2)
> combine(v1, v2) = [v1; v2]
> curried_combine(v1) = v2 -> combine(v1, v2)
> 
> e1_f = map(curried_combine, e1)
> f_flatmap(f) = TypeClasses.map(v2 -> f(v2), e2)
> f_flatmap, e1_f
> end
> prepare_test (generic function with 1 method)
> 
> julia>
> 
> julia> f_flatmap, e1_f = prepare_test(e1, e2)
> (f_flatmap, Eff(effectful=NoEffect{var"#14#18"{Vector{Int64}, var"#combine#16"}}(var"#14#18"{Vector{Int64}, var"#combine#16"}([1], var"#combine#16"())), length(cont)=0))
> 
> julia> @inferred TypeClasses.flatmap(f_flatmap, e1_f) # infers perfectly
> Eff(effectful=NoEffect{Vector{Int64}}([1, 20]), length(cont)=0)
> 
> julia> function test_infers(e1, e2)
> f_flatmap, e1_f = prepare_test(e1, e2)
> TypeClasses.flatmap(f_flatmap, e1_f)
> end
> test_infers (generic function with 1 method)
> 
> julia>
> 
> julia> @inferred test_infers(e1, e2) # infers perfectly
> Eff(effectful=NoEffect{Vector{Int64}}([1, 20]), length(cont)=0)
> 
> julia> function prepare_test(e1, e2)
> combine(v1, v2) = [v1; v2]
> curried_combine(v1) = v2 -> combine(v1, v2)
> 
> e1_f = map(curried_combine, e1)
> f_flatmap(f) = TypeClasses.map(v2 -> f(v2), e2)
> f_flatmap, e1_f
> end
> prepare_test (generic function with 1 method)
> 
> julia> function test_infers(e1, e2)
> f_flatmap, e1_f = prepare_test(e1, e2)
> TypeClasses.flatmap(f_flatmap, e1_f)
> end
> test_infers (generic function with 1 method)
> 
> julia>
> 
> julia> @inferred test_infers(e1, e2) # infers perfectly
> ERROR: return type ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}} does not match inferred return type ExtensibleEffects.Eff
> Stacktrace:
> [1] error(s::String)
> @ Base .\error.jl:33
> [2] top-level scope
> @ REPL[26]:100: 
> 
> ```

> **Copy and pasting this into my Julia 1.7.2 REPL results in \`@inferred test\_infers(e1, e2)\` failing.**
>
> ```julia
> using Pkg
> Pkg.activate(; temp = true)
> Pkg.add(["ExtensibleEffects", "TypeClasses", "Test"])
> 
> using ExtensibleEffects
> using TypeClasses
> using Test
> 
> vector_of_eff_of_vector = map(x -> noeffect([x]), [1, 20])
> 
> e1 = vector_of_eff_of_vector[1]
> e2 = vector_of_eff_of_vector[2]
> 
> function prepare_test(e1, e2)
> combine(v1, v2) = [v1; v2]
> curried_combine(v1) = v2 -> combine(v1, v2)
> 
> e1_f = map(curried_combine, e1)
> f_flatmap(f) = TypeClasses.map(v2 -> f(v2), e2)
> f_flatmap, e1_f
> end
> 
> f_flatmap, e1_f = prepare_test(e1, e2)
> 
> @inferred TypeClasses.flatmap(f_flatmap, e1_f) # infers perfectly
> 
> function test_infers(e1, e2)
> f_flatmap, e1_f = prepare_test(e1, e2)
> TypeClasses.flatmap(f_flatmap, e1_f)
> end
> 
> @inferred test_infers(e1, e2) # infers perfectly
> 
> curried_combine(v1) = v2 -> combine(v1, v2)
> 
> e1_f = map(curried_combine, e1)
> 
> f_flatmap, e1_f
> 
> function prepare_test(e1, e2)
> combine(v1, v2) = [v1; v2]
> curried_combine(v1) = v2 -> combine(v1, v2)
> 
> e1_f = map(curried_combine, e1)
> f_flatmap(f) = TypeClasses.map(v2 -> f(v2), e2)
> f_flatmap, e1_f
> end
> 
> f_flatmap, e1_f = prepare_test(e1, e2)
> 
> @inferred TypeClasses.flatmap(f_flatmap, e1_f) # infers perfectly
> 
> function test_infers(e1, e2)
> f_flatmap, e1_f = prepare_test(e1, e2)
> TypeClasses.flatmap(f_flatmap, e1_f)
> end
> 
> @inferred test_infers(e1, e2) # infers perfectly
> 
> function prepare_test(e1, e2)
> combine(v1, v2) = [v1; v2]
> curried_combine(v1) = v2 -> combine(v1, v2)
> 
> e1_f = map(curried_combine, e1)
> f_flatmap(f) = TypeClasses.map(v2 -> f(v2), e2)
> f_flatmap, e1_f
> end
> 
> function test_infers(e1, e2)
> f_flatmap, e1_f = prepare_test(e1, e2)
> TypeClasses.flatmap(f_flatmap, e1_f)
> end
> @inferred test_infers(e1, e2) # now it breaks?
> 
> ```

The session is reproducible on multiple computers. There appears to be a bug with the inference engine.

> **Minimized script resulting in broken inference**
>
> ```julia
> using Pkg
> Pkg.activate(; temp = true)
> Pkg.add(["ExtensibleEffects", "TypeClasses", "Test"])
> 
> using ExtensibleEffects
> using TypeClasses
> using Test
> 
> vector_of_eff_of_vector = map(x -> noeffect([x]), [1, 20])
> 
> e1 = vector_of_eff_of_vector[1]
> e2 = vector_of_eff_of_vector[2]
> 
> function prepare_test(e1, e2)
> combine(v1, v2) = [v1; v2]
> curried_combine(v1) = v2 -> combine(v1, v2)
> 
> e1_f = map(curried_combine, e1)
> f_flatmap(f) = TypeClasses.map(v2 -> f(v2), e2)
> f_flatmap, e1_f
> end
> 
> function test_infers(e1, e2)
> f_flatmap, e1_f = prepare_test(e1, e2)
> TypeClasses.flatmap(f_flatmap, e1_f)
> end
> 
> @inferred test_infers(e1, e2) # now it breaks?
> 
> ```

> **Minimal script where inference succeeds. Running \`TypeClasses.flatmap(...)\` has a side effect.**
>
> ```julia
> using Pkg
> Pkg.activate(; temp = true)
> Pkg.add(["ExtensibleEffects", "TypeClasses", "Test"])
> 
> using ExtensibleEffects
> using TypeClasses
> using Test
> 
> vector_of_eff_of_vector = map(x -> noeffect([x]), [1, 20])
> 
> e1 = vector_of_eff_of_vector[1]
> e2 = vector_of_eff_of_vector[2]
> 
> function prepare_test(e1, e2)
> combine(v1, v2) = [v1; v2]
> curried_combine(v1) = v2 -> combine(v1, v2)
> 
> e1_f = map(curried_combine, e1)
> f_flatmap(f) = TypeClasses.map(v2 -> f(v2), e2)
> f_flatmap, e1_f
> end
> 
> TypeClasses.flatmap(prepare_test(e1, e2)...) # infers perfectly
> 
> function test_infers(e1, e2)
> f_flatmap, e1_f = prepare_test(e1, e2)
> TypeClasses.flatmap(f_flatmap, e1_f)
> end
> 
> @inferred test_infers(e1, e2) # infers perfectly
> 
> ```

> **Minimal script where inference succeeds at first, and then fails. Redefining \`prepare\_test\` invalidates the side effect.**
>
> ```julia
> using Pkg
> Pkg.activate(; temp = true)
> Pkg.add(["ExtensibleEffects", "TypeClasses", "Test"])
> 
> using ExtensibleEffects
> using TypeClasses
> using Test
> 
> vector_of_eff_of_vector = map(x -> noeffect([x]), [1, 20])
> 
> e1 = vector_of_eff_of_vector[1]
> e2 = vector_of_eff_of_vector[2]
> 
> function prepare_test(e1, e2)
> combine(v1, v2) = [v1; v2]
> curried_combine(v1) = v2 -> combine(v1, v2)
> 
> e1_f = map(curried_combine, e1)
> f_flatmap(f) = TypeClasses.map(v2 -> f(v2), e2)
> f_flatmap, e1_f
> end
> 
> TypeClasses.flatmap(prepare_test(e1, e2)...) # infers perfectly
> 
> function test_infers(e1, e2)
> f_flatmap, e1_f = prepare_test(e1, e2)
> TypeClasses.flatmap(f_flatmap, e1_f)
> end
> 
> @inferred test_infers(e1, e2) # infers perfectly
> 
> function prepare_test(e1, e2)
> combine(v1, v2) = [v1; v2]
> curried_combine(v1) = v2 -> combine(v1, v2)
> 
> e1_f = map(curried_combine, e1)
> f_flatmap(f) = TypeClasses.map(v2 -> f(v2), e2)
> f_flatmap, e1_f
> end
> 
> @inferred test_infers(e1, e2) # now it breaks?
> 
> ```

---

_[View the full topic](https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056)._
