# 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:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)
#### Post date: [May 14, 2022, 11:15am UTC](https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056/1 "2022-05-14T11:15:25Z")

</div>

Hi there, I am the author of [ExtensibleEffects.jl](https://github.com/JuliaFunctional/ExtensibleEffects.jl).

This is an advanced functional package, hence please bear with me if the following example looks totally alien and not understandable why you ever would like to do something like this. It is a minified version of the actual ExtensibleEffects code, and I am very sorry that I was not able to simplify it anyway further so far. At least it is reproducible and fits into a discourse issue, hence I want to ask for help.

# The Goal

```julia
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]

# some functional monadic helpers to work WITHIN the effects
mygoal(e1, e2) = @syntax_flatmap begin
    v1 = e1
    v2 = e2
    @pure [v1; v2]
end

```

```julia
julia> mygoal(e1, e2)
Eff(effectful=NoEffect{Vector{Int64}}([1, 20]), length(cont)=0)

julia> @inferred mygoal(e1, e2)
ERROR: return type ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}} does not match inferred return type ExtensibleEffects.Eff

julia> @code_warntype mygoal(e1, e2) 
# on the terminal this gives nice color output and will show that only the last step does not infer

```

# Let’s look at details of this type inference (which are not understandable to me)

The fancy syntax above boils down to something like the following

```julia
function test_fails(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)
    TypeClasses.flatmap(f_flatmap, e1_f)
end

@inferred test_fails(e1, e2) # same as before
# ERROR: return type ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}} does not match inferred return type ExtensibleEffects.Eff
@code_warntype test_fails(e1, e2) # same as before

```

It fails analogously.

However a slight variation does not fail:

```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

f_flatmap, e1_f = prepare_test(e1, e2)
@inferred TypeClasses.flatmap(f_flatmap, e1_f) # infers perfectly

```

It also works if these two steps are again put into a function

```julia
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

```

This drives me crazy. I do not understand the reason behind it.

These issues corrupt the performance of ExtensibleEffects.jl at many many places.  
Any help to understand what is going on is highly appreciated ❤.

(using Julia 1.7.1)

---

<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, 12:24pm UTC](https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056/3 "2022-05-14T12:24:26Z")

</div>

I’m at a loss here myself. What’s interesting is that I managed to get the Julia session in a state where `test_infer` fails:

```julia
julia> @inferred test_infers(e1, e2)
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[54]:1

julia> @code_warntype test_infers(e1, e2)
MethodInstance for test_infers(::ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}, ::ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}})
  from test_infers(e1, e2) in Main at REPL[48]:1
Arguments
  #self#::Core.Const(test_infers)
  e1::ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}
  e2::ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}
Locals
  @_4::Int64
  e1_f::ExtensibleEffects.Eff{NoEffect{var"#31#35"{Vector{Int64}, var"#combine#33"}}, Tuple{}}
  f_flatmap::var"#f_flatmap#36"{ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}}
Body::ExtensibleEffects.Eff
1 ─ %1 = Main.prepare_test(e1, e2)::Tuple{var"#f_flatmap#36"{ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}}, ExtensibleEffects.Eff{NoEffect{var"#31#35"{Vector{Int64}, var"#combine#33"}}, Tuple{}}}
│ %2 = Base.indexed_iterate(%1, 1)::Core.PartialStruct(Tuple{var"#f_flatmap#36"{ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}}, Int64}, Any[var"#f_flatmap#36"{ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}}, Core.Const(2)])
│ (f_flatmap = Core.getfield(%2, 1))
│ (@_4 = Core.getfield(%2, 2))
│ %5 = Base.indexed_iterate(%1, 2, @_4::Core.Const(2))::Core.PartialStruct(Tuple{ExtensibleEffects.Eff{NoEffect{var"#31#35"{Vector{Int64}, var"#combine#33"}}, Tuple{}}, Int64}, Any[ExtensibleEffects.Eff{NoEffect{var"#31#35"{Vector{Int64}, var"#combine#33"}}, Tuple{}}, Core.Const(3)])
│ (e1_f = Core.getfield(%5, 1))
│ %7 = TypeClasses.flatmap::Core.Const(TypeClasses.flatmap)
│ %8 = f_flatmap::var"#f_flatmap#36"{ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}}
│ %9 = (%7)(%8, e1_f)::ExtensibleEffects.Eff
└── return %9

```

Compare to a session where it succeeds:

```julia
julia> @code_warntype test_infers(e1, e2)
MethodInstance for test_infers(::ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}, ::ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}})
  from test_infers(e1, e2) in Main at REPL[53]:1
Arguments
  #self#::Core.Const(test_infers)
  e1::ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}
  e2::ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}
Locals
  @_4::Int64
  e1_f::ExtensibleEffects.Eff{NoEffect{var"#53#57"{Vector{Int64}, var"#combine#55"}}, Tuple{}}
  f_flatmap::var"#f_flatmap#58"{ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}}
Body::ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}
1 ─ %1 = Main.prepare_test(e1, e2)::Tuple{var"#f_flatmap#58"{ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}}, ExtensibleEffects.Eff{NoEffect{var"#53#57"{Vector{Int64}, var"#combine#55"}}, Tuple{}}}
│ %2 = Base.indexed_iterate(%1, 1)::Core.PartialStruct(Tuple{var"#f_flatmap#58"{ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}}, Int64}, Any[var"#f_flatmap#58"{ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}}, Core.Const(2)])
│ (f_flatmap = Core.getfield(%2, 1))
│ (@_4 = Core.getfield(%2, 2))
│ %5 = Base.indexed_iterate(%1, 2, @_4::Core.Const(2))::Core.PartialStruct(Tuple{ExtensibleEffects.Eff{NoEffect{var"#53#57"{Vector{Int64}, var"#combine#55"}}, Tuple{}}, Int64}, Any[ExtensibleEffects.Eff{NoEffect{var"#53#57"{Vector{Int64}, var"#combine#55"}}, Tuple{}}, Core.Const(3)])
│ (e1_f = Core.getfield(%5, 1))
│ %7 = TypeClasses.flatmap::Core.Const(TypeClasses.flatmap)
│ %8 = f_flatmap::var"#f_flatmap#58"{ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}}
│ %9 = (%7)(%8, e1_f)::ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}}
└── return %9

```

---

<div class="post-metadata">

### Author: ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)
#### Post date: [May 14, 2022, 12:49pm UTC](https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056/4 "2022-05-14T12:49:10Z")

</div>

please try to leave this thread uncluttered. I already tried to summarize everything as needed, so that people do not need to post macroexpand or the output of @code\_warntype itself. It just makes it harder for others to follow.

I rather look for help or reproducible variations which can further enlighten the case.

@mkitti can you please put your large codeblocks into `<details> </details>` blocks, so that this thread gets a bit easier to read?

---

<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, 12:55pm UTC](https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056/5 "2022-05-14T12:55:52Z")

</div>

I’m going to leave the second post as is because it appears that the inference engine is inconsistent.

---

<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?
> 
> ```

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [May 14, 2022, 9:55pm UTC](https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056/7 "2022-05-14T21:55:23Z")

</div>

> [@mkitti](#):
>
> it appears that the inference engine is inconsistent.

Reminiscent of [this thread](https://discourse.julialang.org/t/why-is-this-expression-type-unstable/80648), where I’ve just now also confirmed the effect where redefining a callee function causes the caller function to be inferred as type-unstable again. `Test.@inferred` is actually trickier than `@code_warntype` in that thread’s example, the order of running+compiling the callee and caller methods affects whether `@inferred` gets “stuck” reporting an instability, and the trick of redefining a method to “undo” its compilation can be used to fix that compilation order and make `@inferred` work.

---

<div class="post-metadata">

### Author: ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)
#### Post date: [May 15, 2022, 8:09am UTC](https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056/8 "2022-05-15T08:09:59Z")

</div>

Thank you @mkitti that executing `TypeClasses.flatmap(prepare_test(e1, e2)...) ` somehow introduces a type-inference side-effect is very impressive and mind-blowing.

I wish we could have some Julia core developer commenting on this type-inference behaviour

---

<div class="post-metadata">

### Author: ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)
#### Post date: [May 15, 2022, 8:15am UTC](https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056/9 "2022-05-15T08:15:27Z")

</div>

@tim.holy @kristoffer.carlsson can you take a look?

---

<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 15, 2022, 1:36pm UTC](https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056/10 "2022-05-15T13:36:47Z")

</div>

Maybe we should try [GitHub - timholy/SnoopCompile.jl: Making packages work faster with more extensive precompilation](https://github.com/timholy/SnoopCompile.jl)

---

<div class="post-metadata">

### Author: ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)
#### Post date: [May 20, 2022, 9:44am UTC](https://discourse.julialang.org/t/need-help-with-incomprehensible-typeinference/81056/11 "2022-05-20T09:44:01Z")

</div>

I posted this issue now on Julia github, summarizing it that the typeinference is dependend on the exeuction order.

Big thank you @mkitti for your help to reach this understanding.

> <https://github.com/JuliaLang/julia/issues/45388>
>
> Hi there,
> 
> this issue was already discussed on \[discourse\](https://discourse.j…ulialang.org/t/need-help-with-incomprehensible-typeinference/81056). While we could gather some clarification, it couldn't be solved. Hence I raise it as an issue here.
> 
> I am the author of \[ExtensibleEffects.jl\](https://github.com/JuliaFunctional/ExtensibleEffects.jl) and \[TypeClasses.jl\](https://github.com/JuliaFunctional/TypeClasses.jl) and experience crucial performance difficulties due to bad type inference. As I got multiple requests from the community whether ExtensibleEffects.jl could be made fast, I want to tackle these problems. 
> 
> ExtensibleEffects.jl is an advanced functional package, hence please bear with me if the following example looks not understandable why you ever would like to do something like this. It is a minified version of the actual ExtensibleEffects code, and I am very sorry that I was not able to simplify it anyway further so far. At least it is reproducible and fits into an issue.
> 
> \# My Motivation
> \<details\>
> \<summary\>if you want to understand the motivation for the example, this might help\</summary\>
> \`\`\`julia
> 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\]
> 
> \# some functional monadic helpers to work WITHIN the effects
> mygoal(e1, e2) = @syntax\_flatmap begin
> v1 = e1
> v2 = e2
> @pure \[v1; v2\]
> end
> \`\`\`
> \`\`\`julia
> julia\> mygoal(e1, e2)
> Eff(effectful=NoEffect{Vector{Int64}}(\[1, 20\]), length(cont)=0)
> 
> julia\> @inferred mygoal(e1, e2)
> ERROR: return type ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}} does not match inferred return type ExtensibleEffects.Eff
> 
> julia\> @code\_warntype mygoal(e1, e2) 
> \# on the terminal this gives nice color output and will show that only the last step does not infer
> \`\`\`
> 
> The case boils down to something like the following
> \`\`\`julia
> function test\_fails(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)
> TypeClasses.flatmap(f\_flatmap, e1\_f)
> end
> 
> @inferred test\_fails(e1, e2) # same as before
> \# ERROR: return type ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}} does not match inferred return type ExtensibleEffects.Eff
> @code\_warntype test\_fails(e1, e2) # same as before
> \`\`\`
> If this could be stabilized, much is gained for ExtensibleEffects.jl
> \</details\>
> 
> \# Same code, two execution orders, one fails, the other infers
> 
> Here the one which works
> \`\`\`julia
> 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
> \`\`\`
> 
> And here the one which fails
> \`\`\`julia
> 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)
> \# ERROR: return type ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}} does not match inferred return type ExtensibleEffects.Eff
> 
> f\_flatmap, e1\_f = prepare\_test(e1, e2)
> @inferred TypeClasses.flatmap(f\_flatmap, e1\_f) 
> \# ERROR: return type ExtensibleEffects.Eff{NoEffect{Vector{Int64}}, Tuple{}} does not match inferred return type ExtensibleEffects.Eff
> \`\`\`
> 
> It seems that a top-level call to \`TypeClasses.flatmap\` at the right place informs the compiler about things it usually does not have available (but should have available).
> 
> \------------
> 
> This drives me crazy :smile: I feel like a little child: 10 years programming experience are not enough to solve this on my own, I am depending on you deep core Julia developers and hope someone recognizes what is going on here.
> 
> \<details\>
> \<summary\>(tested on Julia 1.7.1 and Julia 1.8.0-beta3.4)\</summary\>
> \`\`\`julia
> julia\> versioninfo()
> Julia Version 1.7.1
> Commit ac5cc99908 (2021-12-22 19:35 UTC)
> Platform Info:
> OS: Linux (x86\_64-pc-linux-gnu)
> CPU: Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz
> WORD\_SIZE: 64
> LIBM: libopenlibm
> LLVM: libLLVM-12.0.1 (ORCJIT, icelake-client)
> \`\`\`
> 
> \`\`\`julia
> julia\> versioninfo()
> Julia Version 1.8.0-beta3.4
> Commit a4e69c5088 (2022-05-20 09:32 UTC)
> Platform Info:
> OS: Linux (x86\_64-unknown-linux-gnu)
> CPU: 8 × Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz
> WORD\_SIZE: 64
> LIBM: libopenlibm
> LLVM: libLLVM-13.0.1 (ORCJIT, icelake-client)
> Threads: 1 on 8 virtual cores
> Environment:
> LD\_LIBRARY\_PATH = /run/opengl-driver/lib:/run/opengl-driver-32/lib:/usr/lib:/usr/lib32:/nix/store/0fih0yvy9lwxkaaci06gw0x1f5a5aqld-sane-config/lib/sane
> \`\`\`
> \</details\>
