Dispatch on enum

Hey all :slight_smile:

Is it possible to dispatch a function on different enum instances.

Something like this:

julia> @enum E a b

julia> f(::Type{a}) = :a

julia> f(::Type{b}) = :b

julia> f(a)
:a
1 Like

use types.

1 Like

You can get pretty close using value types:

julia> @enum E a b

julia> f(::Val{a}) = :a

julia> f(::Val{b}) = :b

julia> f(x::E) = f(Val(x))

julia> f(a)
:a
2 Likes

Note that this forces Julia to do a runtime dispatch in general (unless x is a compile-time constant), which is slow. Julia’s dispatch is really optimized for code in which the types are mostly inferred (constants) at compile time.

If you have runtime data in an @enum, you should probably be using if statements or similar, rather than dispatching on Val, or you should alternatively be re-thinking your code structure to put the information in the type domain to start with.

6 Likes

I totally agree, and I wouldn’t do what OP is asking myself. But it may still be convenient in some cases, and one might get away with constant propagation in the particular case the enum instances are treated as constants.

That’s cool!

IMO the key strength of multiple dispatch is not neat syntax like

f(::a) = ...
f(::b) = ...
f(::c) = ...

This syntax is nice to have but not essential to me.
Instead the super power of multiple dispatch is openness.
You can extend YourLib.f(::MyType) ... anywhere. This openness enables
exceptional code reuse in julia, see also this talk.
Now in case of enums, all instances are fixed, when the enum is defined.
So there is no openness benefit of dispatch over good old if statements.
So instead of

f(::a) = ...
f(::b) = ...
f(::c) = ...

I would recommend

function f(x::E)
    if x === a
        ...
    elseif x === b
        ...
    elseif x === c
        ...
    else
        error("Unreachable")
    end
end

It looks not as neat, but is equally powerful. If you really insist on the neat syntax I would use a macro to transform it into the if else chain.

2 Likes

I agree with @jw3126, and I would like to add: if you want to use the multiple dispatch for openness, then you are better either using Val{Symbol} (simple but collision prone) or just define an abstract type E and let anyone to create subtypes to represent the values (even empty struct A <: E; end will work for dispatch). In fact, the abstract type is not even needed (as each user is supposed to write a method for their own concrete type) and Any could be used instead, but it may help debugging by restricting some generic methods to the abstract supertype (and catching when the user sends complete nonsense instead of their own struct-as-enum-value).