Can I use `@eval` to dynamically define methods for dispatch on `Val`s?

Hi all,

I’m trying to dynamically define methods for a function to dispatch on specific names (symbols/strings) which seems to work outside a loop, but not inside a loop.

Can someone explain how I may be mis-applying the metaprogramming functionality of Julia here?

               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.6.0 (2021-03-24)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> for name in [:aye, :bee, :cee]
           @eval Main begin
               function fcn(::Val{name})
                   println(name)
               end
           end
       end
ERROR: UndefVarError: name not defined
Stacktrace:
 [1] top-level scope
   @ REPL[1]:3
 [2] eval(m::Module, e::Any)
   @ Core .\boot.jl:360
 [3] top-level scope
   @ REPL[1]:2

julia> new_name = :dee
:dee

julia> @eval Main begin
           function gcn(::Val{new_name})
               println(new_name)
           end
       end
gcn (generic function with 1 method)

julia> gcn(Val(:dee))
dee

You need to escape name, so

for name in [:aye, :bee, :cee]
    @eval Main begin
        function fcn(::Val{$name})
            println($name)
        end
    end
end

Otherwise it is trying to look up name when you run the method instead of when you define it.

2 Likes

My apologies, I forgot to mention that I did try this as well, and it didn’t work.

julia> for name in [:aye, :bee, :cee]
    @eval Main begin
        function fcn(::Val{$name})
            println($name)
        end
    end
end

ERROR: UndefVarError: aye not defined
Stacktrace:
 [1] top-level scope
   @ REPL[1]:3
 [2] eval
   @ .\boot.jl:360 [inlined]
 [3] top-level scope
   @ .\REPL[1]:2

Thank you though! I figured it out and yeah what you suggested is one step closer, this is what works:

julia> for name in [:(:aye), :(:bee), :(:cee)]
    @eval Main begin
        function fcn(::Val{$name})
            println($name)
        end
    end
end

julia> fcn(Val(:aye))
aye

It’s just strange that is complains that name is undefined in my OP example.

2 Likes

Another option is:

for name in [:aye, :bee, :cee]
    @eval Main begin
        function fcn(::Val{$(QuoteNode(name))})
            println($(QuoteNode(name)))
        end
    end
end
1 Like