# Dispatch based on symbol parameter

**URL:** <https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849>\
**Category:** Performance\
**Created:** [February 27, 2024, 2:52pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849 "2024-02-27T14:52:14Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![atteson](https://avatars.discourse-cdn.com/v4/letter/a/91b2a8/32.png) [@atteson](https://discourse.julialang.org/u/atteson)\
**Post date:** [February 27, 2024, 2:52pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/1 "2024-02-27T14:52:15Z")

</div>

Is there any way to dispatch directly on a symbol function parameter? I have a function which takes a symbol and returns a different type based on a symbol that is passed into it. In order for this function to have a type stable output (whether explicitly or inferred), it would need to dispatch based on the symbol parameter. I realize this can be done with Val but I was hoping to avoid Val. I was hoping for something that look like this:

```julia
f( t::NamedTuple{T,U}, s::Symbol ) = t[s]
@code_warntype f((a=1,b=1.0), :a)

```

which returns a union. The undesirable but type stable Val version looks like this:

```julia
f( t::NamedTuple{T,U}, ::Val{V} ) = t[V]
@code_warntype f((:a=1,:b=1.0), Val(:a))

```

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [February 27, 2024, 3:00pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/2 "2024-02-27T15:00:11Z")

</div>

Is this what you intend?

```julia
function dispatch(t::NamedTuple{(:a, :b)}, s::Symbol)
    t[s]
end

nt = (a = 1, b = 2)
dispatch(nt, :a) # 1
dispatch(nt, :b) # 2
dispatch(nt, :c) # error

```

---

<div class="post-metadata">

**Author:** ![atteson](https://avatars.discourse-cdn.com/v4/letter/a/91b2a8/32.png) [@atteson](https://discourse.julialang.org/u/atteson)\
**Post date:** [February 27, 2024, 3:09pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/3 "2024-02-27T15:09:29Z")

</div>

No. The issue with that is that it returns a union which makes code which calls it significantly less effiicient.

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [February 27, 2024, 3:18pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/4 "2024-02-27T15:18:36Z")

</div>

Are you sure that it is significantly less efficient?  
For example,

```julia
nt = (a = 1, b = 2.0)
@code_warntype nt.a # or getfield(nt, :a)

```

also shows the same `Union{Int64,Float64}`. My understanding is that this might get optimized away eventually, due to propagation of constants.

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [February 27, 2024, 3:23pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/5 "2024-02-27T15:23:56Z")

</div>

Unions of two or three bitstypes are very performant, and they have been for a while. Unions of two concrete types are reasonable, too.

---

<div class="post-metadata">

**Author:** ![atteson](https://avatars.discourse-cdn.com/v4/letter/a/91b2a8/32.png) [@atteson](https://discourse.julialang.org/u/atteson)\
**Post date:** [February 27, 2024, 3:24pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/6 "2024-02-27T15:24:11Z")

</div>

Well, I’m not sure because this is highly simplified sample code. In the actual code that I’m using, it ends up returning Any and so is significantly less efficient.

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [February 27, 2024, 3:24pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/7 "2024-02-27T15:24:52Z")

</div>

Returning Any is not a good approach. What is causing that?

---

<div class="post-metadata">

**Author:** ![atteson](https://avatars.discourse-cdn.com/v4/letter/a/91b2a8/32.png) [@atteson](https://discourse.julialang.org/u/atteson)\
**Post date:** [February 27, 2024, 3:25pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/8 "2024-02-27T15:25:27Z")

</div>

A much larger named tuple.

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [February 27, 2024, 3:26pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/9 "2024-02-27T15:26:04Z")

</div>

How many distinct types of values occur in that named tuple?

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [February 27, 2024, 3:27pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/10 "2024-02-27T15:27:20Z")

</div>

You might use a few structs that hold values of several types rather than many different types.

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [February 27, 2024, 3:31pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/11 "2024-02-27T15:31:46Z")

</div>

I think the confusion here is how `@code_warntype` works.

`@code_warntypes` looks at the function that is called and infers based on the input types what happens. Meaning, it will **not** propagate the information that `:a` is a constant at compile time, it only assume the input is some `Symbol`. This is why it shows the `Union` even for something like `nt.a` which is definitely type stable.

My brain is too slow to come up with a proper setting to demonstrate it, but essentially, wrap the example into a function and it is fine, like

```julia
f(nt, s) = nt[s]
g(nt, x) = f(nt, :a) * x

@code_warntype g(nt, 2)

```

---

<div class="post-metadata">

**Author:** ![atteson](https://avatars.discourse-cdn.com/v4/letter/a/91b2a8/32.png) [@atteson](https://discourse.julialang.org/u/atteson)\
**Post date:** [February 27, 2024, 3:36pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/12 "2024-02-27T15:36:39Z")

</div>

Thanks for that. Unfortunately, in my real code, it is not even returning a Union but I can dig further into why that is.

---

<div class="post-metadata">

**Author:** ![atteson](https://avatars.discourse-cdn.com/v4/letter/a/91b2a8/32.png) [@atteson](https://discourse.julialang.org/u/atteson)\
**Post date:** [February 27, 2024, 3:52pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/13 "2024-02-27T15:52:55Z")

</div>

It seems that when I go to 4 types, it returns Any:

```julia
f( t::NamedTuple{U,V}, s::Symbol ) where {U,V} = t[s]
@code_warntype f((a=1,b=1.0,c=1.0f0,d=0x1), :a)

```

I can use Val in the worst case though I’ll have to put Val almost everywhere in my code. If that’s the only way, thanks to both of you for help. I hadn’t known about constant propagation in julia.

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [February 27, 2024, 4:53pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/14 "2024-02-27T16:53:20Z")

</div>

I’m very sorry, but you still using the same misconception of `@code_warntype` for debugging your code. The point is that `@code_warntype` basically tries to assert which type comes out if you call `f(::@NamedTuple{a::Int64, b::Float64, c::Float32, d::UInt8}, ::Symbol)`.

This operation is of course not type-stable and the union splitting stops after a while. Using `Val` here fixes that issue as `f(::..., ::Val{:a})` contains the symbol and make the warning disappear.

However, in the setting when you want to use the function, there is the major advantage that you would write something like `f(nt, :a)`, which means that Julia has more information whenever it comes across this line. It is exactly the same reason why `nt.a` is type-stable in the first place!

Consider this example:

```julia
nt = (a=1,b=1.0,c=1.0f0,d=0x1, e=2)

f(nt, s) = nt[s]

function something(nt)
      return f(nt, :a) + f(nt, :e)
end

@code_warntype something(nt)

```

As you see, it knows for the function `f` suddenly the correct return types:

```julia
1 ─ %1 = Main.f(nt, :a)::Int64
│ %2 = Main.f(nt, :e)::Int64

```

That is the situation you will be in when actually using the function.

Of course, something like

```julia
map( nt[s], (:a, :e) )

```

is type-unstable and would only be type-stable if you use `Val` types. But that is hopefully not the situation you are in…

---

<div class="post-metadata">

**Author:** ![atteson](https://avatars.discourse-cdn.com/v4/letter/a/91b2a8/32.png) [@atteson](https://discourse.julialang.org/u/atteson)\
**Post date:** [February 28, 2024, 2:48pm UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/15 "2024-02-28T14:48:49Z")

</div>

That’s interesting. Thanks. I had assumed that once julia compiles a function for a given concrete type, it never recompiles or reoptimizes that function. I guess that’s a bad assumption. Anyway, in my actual use case, which is much more complex, it does not work out the types.

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [February 29, 2024, 5:40am UTC](https://discourse.julialang.org/t/dispatch-based-on-symbol-parameter/110849/16 "2024-02-29T05:40:37Z")

</div>

Note that constant-propagation is the only reason `my_tuple[1]` and `my_struct.a` (which call `getindex` and `getproperty` respectively) are type-stable. If the index or property name is not a constant in the code, then it will not be type-stable unless the content types form a narrow union.

If constant propagation isn’t happening, you might nudge the compiler with the `Base.@constprop` macro. For example:

```julia
Base.@constprop :aggressive function my_func(a, b)
    #= ... =#
end

```
