# Using @nospecialize on a set of subtypes

**URL:** <https://discourse.julialang.org/t/using-nospecialize-on-a-set-of-subtypes/45655>\
**Category:** General Usage\
**Created:** [August 28, 2020, 4:13am UTC](https://discourse.julialang.org/t/using-nospecialize-on-a-set-of-subtypes/45655 "2020-08-28T04:13:24Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![colintbowers](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/colintbowers/32/8033_2.png) [@colintbowers](https://discourse.julialang.org/u/colintbowers)\
**Post date:** [August 28, 2020, 4:13am UTC](https://discourse.julialang.org/t/using-nospecialize-on-a-set-of-subtypes/45655/1 "2020-08-28T04:13:24Z")

</div>

Hi all,

I have a function `g(a,b)` with many methods based on input types of `a` and `b`. A subset of the methods have common code, eg:

```julia
g(a::T1, b::T2) where {T1<:AbstractA,T2<:AbstractB} = (fa(a),fb(b))

```

where `fa` and `fb` will specialize on the input type, but clearly `g` does not need to specialize on the input type, other than to ensure that `a` is a subtype of `AbstractA` and `b` is a subtype of `AbstractB`.

I’d like to tell the compiler to just compile `g` once, rather than compiling for every possible combination of subtypes of `AbstractA` and `AbstractB`. My understanding is that I do this using `@nospecialize`. However, I can’t seem to get it to work properly. Specifically the following _does_ work:

```julia
g(@nospecialize(a), @nospecialize(b)) = (fa(a), fb(b))

```

but this does not restrict `a` to be a subtype of `AbstractA` and `b` to be a subtype of `AbstractB`. I could also try the following:

```julia
g(@nospecialize(a::T1), @nospecialize(b::T2)) where {T1<:AbstractA,T2<:AbstractB} = (fa(a),fb(b))

```

but based on my MWE below this appears to compile for every combination of subtype of `AbstractA` and `AbstractB`.

Apologies, there is probably a smarter way to test this but I don’t know what it is. I had to resort to building 10 subtypes of abstract types `AbA`, `AbB`, and `AbC`, and then looping over calls to a trivial function for every combination of subtypes. I did this for four different functions below `f1`, `f2`, `f3`, and `f4`, and hoped that the loop over calls to `f3` and `f4` would be fast. In practice, only the loop over `f4` is fast. Any ideas how I can speed up the `f3` case so it works like the `f4` case, while preserving the subtype restrictions? Code below:

```julia
let
    K = 10;
    abstract type AbA ; end
    for ka = 1:K
        namesymbol = Symbol("A$(ka)")
        @eval struct $(namesymbol) <: AbA ; x::Int ; end
        @eval $(Symbol("fa"))(x::$(namesymbol))::Int = x.x + $(ka)
    end
    abstract type AbB ; end
    for kb = 1:K
        namesymbol = Symbol("B$(kb)")
        @eval struct $(namesymbol) <: AbB ; x::Int ; end
        @eval $(Symbol("fb"))(x::$(namesymbol))::Int = x.x + $(kb)
    end
    abstract type AbC ; end
    for kc = 1:K
        namesymbol = Symbol("C$(kc)")
        @eval struct $(namesymbol) <: AbC ; x::Int ; end
        @eval $(Symbol("fc"))(x::$(namesymbol))::Int = x.x + $(kc)
    end
    function f1(a::Ta, b::Tb, c::Tc) where {Ta<:AbA,Tb<:AbB,Tc<:AbC}
        return fa(a) + fb(b) + fc(c)
    end
    function f2(a, b, c)
        return fa(a) + fb(b) + fc(c)
    end
    function f3(@nospecialize(a::Ta), @nospecialize(b::Tb), @nospecialize(c::Tc))::Float64 where {Ta<:AbA,Tb<:AbB,Tc<:AbC}
        return fa(a) + fb(b) + fc(c)
    end
    function f4(@nospecialize(a), @nospecialize(b), @nospecialize(c))
        return fa(a) + fb(b) + fc(c)
    end
    println("working on f1")
    v1 = fill(0, K*K*K);
    for ka = 1:K ; for kb = 1:K ; for kc = 1:K
        a = @eval $(Symbol("A$(ka)"))(rand(1:10))
        b = @eval $(Symbol("B$(kb)"))(rand(1:10))
        c = @eval $(Symbol("C$(kc)"))(rand(1:10))
        v1[ka*kb*kc] = f1(a, b, c)
    end ; end ; end
    println("working on f2")
    v2 = fill(0, K*K*K);
    for ka = 1:K ; for kb = 1:K ; for kc = 1:K
        a = @eval $(Symbol("A$(ka)"))(rand(1:10))
        b = @eval $(Symbol("B$(kb)"))(rand(1:10))
        c = @eval $(Symbol("C$(kc)"))(rand(1:10))
        v2[ka*kb*kc] = f2(a, b, c)
    end ; end ; end
    println("working on f3")
    v3 = fill(0, K*K*K);
    for ka = 1:K ; for kb = 1:K ; for kc = 1:K
        a = @eval $(Symbol("A$(ka)"))(rand(1:10))
        b = @eval $(Symbol("B$(kb)"))(rand(1:10))
        c = @eval $(Symbol("C$(kc)"))(rand(1:10))
        v3[ka*kb*kc] = f3(a, b, c)
    end ; end ; end
    println("working on f4")
    v4 = fill(0, K*K*K);
    for ka = 1:K ; for kb = 1:K ; for kc = 1:K
        a = @eval $(Symbol("A$(ka)"))(rand(1:10))
        b = @eval $(Symbol("B$(kb)"))(rand(1:10))
        c = @eval $(Symbol("C$(kc)"))(rand(1:10))
        v4[ka*kb*kc] = f4(a, b, c)
    end ; end ; end
end

```

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [August 28, 2020, 6:57am UTC](https://discourse.julialang.org/t/using-nospecialize-on-a-set-of-subtypes/45655/2 "2020-08-28T06:57:24Z")

</div>

Because the TypeVars `T1` and `T2` connect to something outside the `@nospecialize` scope you’ve essentially defeated the mechanism. If you write `g` as

```julia
g(@nospecialize(a::AbstractA), @nospecialize(b::AbstractB)) = ...

```

then I think you’ll get what you’re aiming for.

---

<div class="post-metadata">

**Author:** ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)\
**Post date:** [August 28, 2020, 10:16am UTC](https://discourse.julialang.org/t/using-nospecialize-on-a-set-of-subtypes/45655/3 "2020-08-28T10:16:30Z")

</div>

It feels like

```julia
julia> @nospecialize g(a::T1, b::T2) where {T1<:AbstractA,T2<:AbstractB} = (fa(a),fb(b))

```

should work, but worryingly it doesn’t do anything. It doesn’t define a method at all, and does not throw an error.

---

<div class="post-metadata">

**Author:** ![colintbowers](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/colintbowers/32/8033_2.png) [@colintbowers](https://discourse.julialang.org/u/colintbowers)\
**Post date:** [August 30, 2020, 1:03am UTC](https://discourse.julialang.org/t/using-nospecialize-on-a-set-of-subtypes/45655/4 "2020-08-30T01:03:20Z")

</div>

Yes that absolutely works thank you. Gah, I feel like I should have been able to work that one out on my own 🙂

---

<div class="post-metadata">

**Author:** ![colintbowers](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/colintbowers/32/8033_2.png) [@colintbowers](https://discourse.julialang.org/u/colintbowers)\
**Post date:** [August 30, 2020, 1:09am UTC](https://discourse.julialang.org/t/using-nospecialize-on-a-set-of-subtypes/45655/5 "2020-08-30T01:09:51Z")

</div>

Yes, I plugged:

```julia
@nospecialize function f6(a::AbA, b::AbB, c::AbC)
    return fa(a) + fb(b) + fc(c)
end

```

into my MWE and get the same behaviour. Nothing until an `UndefVarError` when I first try to call `f6`. Feels odd…
