# How to mark a function argument for specialization on types while still staying as a generic argument?

**URL:** <https://discourse.julialang.org/t/how-to-mark-a-function-argument-for-specialization-on-types-while-still-staying-as-a-generic-argument/80652>\
**Category:** Performance\
**Tags:** question\
**Created:** [May 7, 2022, 7:35am UTC](https://discourse.julialang.org/t/how-to-mark-a-function-argument-for-specialization-on-types-while-still-staying-as-a-generic-argument/80652 "2022-05-07T07:35:03Z")\
**Posts on this page:** 9\
**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 7, 2022, 7:35am UTC](https://discourse.julialang.org/t/how-to-mark-a-function-argument-for-specialization-on-types-while-still-staying-as-a-generic-argument/80652/1 "2022-05-07T07:35:03Z")

</div>

This is a follow up question which cristallized from [this other discourse topic](https://discourse.julialang.org/t/need-help-on-understanding-type-inference-performance/80600)

# Problem

I am struggeling with improving performance of my package [ExtensibleEffects.jl](https://github.com/JuliaFunctional/ExtensibleEffects.jl) because Julia is not specializing a function on types in certain cases. See [the docs](https://docs.julialang.org/en/v1/manual/performance-tips/#Be-aware-of-when-Julia-avoids-specializing).

For me it feels, this case should not avoid specialization, but anyway, in current Julia this is what happens. Hence I am looking for a way to tell the compiler it should specialize on types, however same time I want to keep my generic function.

I.e. the recommendation from the docs, to just use `myfunc(::Type{T}) where T = ...` in order to trigger specialization does not work for me, because I need my function `myfunc` to stay generic.  
What other way exist to mark a function for type-specialization?

# Example

Here my concrete minimized example which fails to infer for the type input, but runs smoothly for a normal input. It is adapted from ExtensibleEffects. `ChainedFunctions` essentially represents function composition without composing functions, but just by storing functions. As the error already happens without composition, the example feels incomplete, but please take it for granted, that this is useful for ExtensibleEffects.jl.

```julia
struct ChainedFunctions{Fs}
    functions::Fs
    ChainedFunctions(functions...) = new{typeof(functions)}(functions)
end

myconvert(T, value) = T(value)

function myconvert_wrapper(type, value)
    continuation = ChainedFunctions(x -> myconvert(type, x))
    first_func = Base.first(continuation.functions)
    first_func(value)
end

```

and here the test which partly works, and partly fails.

```julia
using Test
tostring(a) = "$a"
@inferred myconvert_wrapper(tostring, 1)
# "1"

@inferred myconvert_wrapper(Symbol, 1)
# ERROR: return type Symbol does not match inferred return type Any

```

---

<div class="post-metadata">

**Author:** ![Stephen\_Vavasis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stephen_vavasis/32/3389_2.png) [@Stephen\_Vavasis](https://discourse.julialang.org/u/Stephen_Vavasis)\
**Post date:** [May 8, 2022, 1:39am UTC](https://discourse.julialang.org/t/how-to-mark-a-function-argument-for-specialization-on-types-while-still-staying-as-a-generic-argument/80652/2 "2022-05-08T01:39:17Z")

</div>

The following seems to work, but it is ugly. Furthermore, I don’t understand why this works while yours does not. Maybe someone can improve this?

```julia
function myconvert_wrapper(t::Union{T,Type{T}}, value) where T
    if isa(t,Type)
        continuation = ChainedFunctions(x -> myconvert(T, x))
    else
        continuation = ChainedFunctions(x -> myconvert(t, x))
    end
    first_func = Base.first(continuation.functions)
    first_func(value)
end

```

---

<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 8, 2022, 7:00am UTC](https://discourse.julialang.org/t/how-to-mark-a-function-argument-for-specialization-on-types-while-still-staying-as-a-generic-argument/80652/3 "2022-05-08T07:00:57Z")

</div>

Hi Stephen, thank you very much

this way I kind of found as well. It works, because you duplicate the decisive code part. The `if isa(t, Type)` works like a subfunction with a clause for `Type{T} where T}` and another normal clause. And the creation of the closure `ChainedFunctions(x -> myconvert(T, x))` is the core thing which fails if not specialized.

I am looking for a solution which does not duplicate the code (and does not use a macro for hidden code duplication either).

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [May 9, 2022, 9:56pm UTC](https://discourse.julialang.org/t/how-to-mark-a-function-argument-for-specialization-on-types-while-still-staying-as-a-generic-argument/80652/4 "2022-05-09T21:56:00Z")

</div>

> [@schlichtanders](#):
>
> I am looking for a solution which does not duplicate the code (and does not use a macro for hidden code duplication either).

why? the point of multi-dispatch is to allow you to write specialized methods. You’re kind of asking people to do it by not doing it.

Also, this example seems to be contrived, in the sense that `myconvert` is designed to deliberately to mix value and Type together, which is something you should avoid irl.

If this is not a XY problem, my suggestion is for you to additionally define this:

```julia
julia> function myconvert_wrapper(::Type{T}, value) where T
           continuation = ChainedFunctions(x -> myconvert(T, x))
           first_func = Base.first(continuation.functions)
           first_func(value)
       end

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [May 9, 2022, 9:58pm UTC](https://discourse.julialang.org/t/how-to-mark-a-function-argument-for-specialization-on-types-while-still-staying-as-a-generic-argument/80652/5 "2022-05-09T21:58:52Z")

</div>

you can also re-organize your code btw:

```julia
julia> F(::Type{T}) where T = x -> myconvert(T, x)

julia> F(t) = x -> myconvert(t, x)

julia> function myconvert_wrapper(t, value)
           continuation = ChainedFunctions(F(t))
           first_func = Base.first(continuation.functions)
           first_func(value)
       end

julia> using Test

julia> @inferred myconvert_wrapper(tostring, 1)
"1"

julia> @inferred myconvert_wrapper(Symbol, 1)
Symbol("1")

```

---

<div class="post-metadata">

**Author:** ![Stephen\_Vavasis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stephen_vavasis/32/3389_2.png) [@Stephen\_Vavasis](https://discourse.julialang.org/u/Stephen_Vavasis)\
**Post date:** [May 10, 2022, 1:48am UTC](https://discourse.julialang.org/t/how-to-mark-a-function-argument-for-specialization-on-types-while-still-staying-as-a-generic-argument/80652/6 "2022-05-10T01:48:35Z")

</div>

I don’t think mixing a function and a type is necessarily discouraged. In particular, the first argument of function `get!` in Base for Dict is `Union{Function,Type}`. As far as I can tell, `get!` specializes properly for both functions and types.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [May 10, 2022, 2:38am UTC](https://discourse.julialang.org/t/how-to-mark-a-function-argument-for-specialization-on-types-while-still-staying-as-a-generic-argument/80652/7 "2022-05-10T02:38:22Z")

</div>

that one is just a callable, again, if you specialize on it, you specialize on it.

I’m saying what we’re doing here to the argument seems unnatural.

Also, the type stability of `get!()` comes from cheating:

> <https://github.com/JuliaLang/julia/blob/e7209e12f749acbdf209827fe2f03f6e1db2b701/base/dict.jl#L489>

it’s stable because we always convert to the type of the values of dict

---

<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 10, 2022, 8:41am UTC](https://discourse.julialang.org/t/how-to-mark-a-function-argument-for-specialization-on-types-while-still-staying-as-a-generic-argument/80652/8 "2022-05-10T08:41:10Z")

</div>

I appreciate sharing my perspective that some argument can be a type or a function or a value, and that generic code should not need to case-distinguish these.

With the current implementation of Julia this is unfortunately necessary for writing closures, as type-specialization does not trigger (does not trigger correctly?). For me writing closures is generic programming.

Thank you all for your input. I will file a feature request/bug report to julialang respectively.

---

<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 10, 2022, 4:41pm UTC](https://discourse.julialang.org/t/how-to-mark-a-function-argument-for-specialization-on-types-while-still-staying-as-a-generic-argument/80652/9 "2022-05-10T16:41:01Z")

</div>

Here is the feature request: [Feature Request: Have a way to specialize on Type without making the argument restricted to Types only · Issue #45266 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/45266)
