# Type Instability when composing functor and function

**URL:** <https://discourse.julialang.org/t/type-instability-when-composing-functor-and-function/103680>\
**Category:** Performance\
**Tags:** flux, machine-learning, code\_warntype, type-stability, neural-network\
**Created:** [September 8, 2023, 8:02pm UTC](https://discourse.julialang.org/t/type-instability-when-composing-functor-and-function/103680 "2023-09-08T20:02:19Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![elenev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elenev/32/18440_2.png) [@elenev](https://discourse.julialang.org/u/elenev)\
**Post date:** [September 8, 2023, 8:02pm UTC](https://discourse.julialang.org/t/type-instability-when-composing-functor-and-function/103680/1 "2023-09-08T20:02:19Z")

</div>

I am training a neural net using Flux.jl. I have a custom function that normalizes inputs before evaluating the model:

```julia
function normalize(state)
    state_min = [-1.2f0, -0.07f0]
    state_max = [0.6f0, 0.07f0]
    normalized_state = (state .- state_min) ./ (state_max .- state_min)
    return normalized_state
end

mdl = Chain(Dense(2 => 32, swish),
    Dense(32 => 32, swish),
    Dense(32 => 1))

```

Define:

```julia
f(x) = mdl(normalize(x))
xtest = [-0.1f0, 0.2f0]

```

Why is `mdl(normalize(xtest))` type-stable while `f(xtest)` is not, returning:

```julia
julia> @code_warntype f(x)
MethodInstance for f(::Vector{Float32})
  from f(x) @ Main Untitled-1:16
Arguments
  #self#::Core.Const(f)
  x::Vector{Float32}
Body::Any
1 ─ %1 = Main.normalize(x)::Vector{Float32}
│ %2 = Main.mdl(%1)::Any
└── return %2

```

---

<div class="post-metadata">

**Author:** ![elenev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elenev/32/18440_2.png) [@elenev](https://discourse.julialang.org/u/elenev)\
**Post date:** [September 8, 2023, 9:22pm UTC](https://discourse.julialang.org/t/type-instability-when-composing-functor-and-function/103680/2 "2023-09-08T21:22:11Z")

</div>

Dug into this a bit more. This seems like a general pattern for functors, not specific to anything in Flux:

```julia
struct MyStruct{T}
    k::T
end

function (S::MyStruct)(x)
    return S.k * x
end

S = MyStruct(2)
f(x) = S(2*x)

```

Here are the `@code_warntype` results:

```julia
julia> @code_warntype S(2*1)
MethodInstance for (::MyStruct{Int64})(::Int64)
  from (S::MyStruct)(x) @ Main Untitled-1:61
Arguments
  S::MyStruct{Int64}
  x::Int64
Body::Int64
1 ─ %1 = Base.getproperty(S, :k)::Int64
│ %2 = (%1 * x)::Int64
└── return %2

julia> @code_warntype f(1)
MethodInstance for f(::Int64)
  from f(x) @ Main Untitled-1:66
Arguments
  #self#::Core.Const(f)
  x::Int64
Body::Any
1 ─ %1 = (2 * x)::Int64
│ %2 = Main.S(%1)::Any
└── return %2

```

---

<div class="post-metadata">

**Author:** ![contradict](https://avatars.discourse-cdn.com/v4/letter/c/ac91a4/32.png) [@contradict](https://discourse.julialang.org/u/contradict)\
**Post date:** [September 8, 2023, 9:32pm UTC](https://discourse.julialang.org/t/type-instability-when-composing-functor-and-function/103680/3 "2023-09-08T21:32:15Z")

</div>

> [@elenev](#):
>
> `S = MyStruct(2)`

This is a consequence of working on global scope where non-constant globals must be Any because the compiler can’t prove they won’t change out from under it.

```julia
julia> const S = MyStruct(2)
MyStruct{Int64}(2)

julia> f(x) = S(2*x)
f (generic function with 1 method)

julia> @code_warntype f(1)
MethodInstance for f(::Int64)
  from f(x) @ Main REPL[4]:1
Arguments
  #self#::Core.Const(f)
  x::Int64
Body::Int64
1 ─ %1 = (2 * x)::Int64
│ %2 = Main.S(%1)::Int64
└── return %2

```

Or in a closure

```julia
julia> function g(y)
       S = MyStruct(y)
       h(x) = S(2*x)
       end
g (generic function with 1 method)

julia> h=g(2)
h (generic function with 1 method)

ulia> @code_warntype h(1)
MethodInstance for (::var"#h#8"{MyStruct{Int64}})(::Int64)
  from (::var"#h#8")(x) @ Main REPL[7]:3
Arguments
  #self#::var"#h#8"{MyStruct{Int64}}
  x::Int64
Body::Int64
1 ─ %1 = Core.getfield(#self#, :S)::MyStruct{Int64}
│ %2 = (2 * x)::Int64
│ %3 = (%1)(%2)::Int64
└── return %3

```

---

<div class="post-metadata">

**Author:** ![elenev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elenev/32/18440_2.png) [@elenev](https://discourse.julialang.org/u/elenev)\
**Post date:** [September 8, 2023, 9:46pm UTC](https://discourse.julialang.org/t/type-instability-when-composing-functor-and-function/103680/4 "2023-09-08T21:46:33Z")

</div>

Thanks! I confirmed that making `mdl` constant in my initial example following your suggestion resolve the issue there too.

Since I always end up declaring stuff in global scope when debugging/trying things out, I guess the lesson is to declare stuff constant when checking for type instability. I suppose BenchmarkTools already does this when one interpolates variables using $, so if one sees excessive allocations when benchmarking, this kind of type instability isn’t to blame.
