# What are the dangers of eval in macros, to get the values of an enum symbol

**URL:** <https://discourse.julialang.org/t/what-are-the-dangers-of-eval-in-macros-to-get-the-values-of-an-enum-symbol/119078>\
**Category:** General Usage\
**Tags:** macros\
**Created:** [September 5, 2024, 3:51pm UTC](https://discourse.julialang.org/t/what-are-the-dangers-of-eval-in-macros-to-get-the-values-of-an-enum-symbol/119078 "2024-09-05T15:51:39Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![NatMath](https://avatars.discourse-cdn.com/v4/letter/n/4491bb/32.png) [@NatMath](https://discourse.julialang.org/u/NatMath)\
**Post date:** [September 5, 2024, 3:51pm UTC](https://discourse.julialang.org/t/what-are-the-dangers-of-eval-in-macros-to-get-the-values-of-an-enum-symbol/119078/1 "2024-09-05T15:51:39Z")

</div>

I’m working on an implementation of the PBRT 4 raytracer in Julia. To avoid having containers of abstract types for primitives, materials, etc, some of the base object types are defined using a tag, instead of subclasses, for example

```julia
@enum Shape SPHERE CUBE
struct Primitive
    shape::Shape
    center::SVector{3, Float64}
    size::Float64 # radius or side-length
end

```

I want to be able to dispatch on the value of the tag. The simplest way I can see is doing this. All “sub-functions” return the same type, which is easily inferable.

```julia
intersect_sphere(p::Primitive, r::Ray) = ...
intersect_cube(p::Primitive, r::Ray) = ...
function intersect(p::Primitive, r::Ray)
    p.shape == SPHERE && return intersect_sphere(p, r)
    p.shape == CUBE && return intersect_cube(p, r)
end

```

Since this is something that I will use a lot, I tried writing a macro to automate this pattern. This also avoids forgetting one of the enum values in the dispatch. However, I have the issue that the macro needs to know about `Shape`, specifically its instances, to generate this code. This is my current implementation:

```julia
macro tagdispatch(enum, f, sig, tag=:tag)
    fname = String(f)
    argsymbs = [gensym(string(T)) for T in sig.args]

    headerexpr = Expr(:call, f, 
        [
            Expr(:(::), s, T) for (s, T) in zip(argsymbs, sig.args)
        ]...
    )

    blockexpr = Expr(:block,
        [
            :($(argsymbs[1]).$tag == $v && 
                return $(Symbol(fname * "_" * lowercase(string(v))))($(argsymbs...))) 
            for v = instances(eval(enum))
        ]...
    )

    esc(Expr(:function, 
        headerexpr,
        blockexpr
    ))
end

@tagdispatch Shape intersect (Primitive, Ray) shape

```

This works, but to get access to the enum from its symbol, I use `eval(enum)`, which I know is generally bad in macros. Specifically, what are the dangers of doing this, considering that in my case the enum will always be defined before the macro is used.

If it is definitely something to avoid, is there another way to solve this, other than write the functions by hand (which I could do, if necessary)?

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [September 5, 2024, 4:27pm UTC](https://discourse.julialang.org/t/what-are-the-dangers-of-eval-in-macros-to-get-the-values-of-an-enum-symbol/119078/2 "2024-09-05T16:27:33Z")

</div>

Looks like you’re in the process of re-creating [SumTypes.jl](https://github.com/MasonProtter/SumTypes.jl)

```julia
using SumTypes, StaticArrays
@sum_type Primitive begin
    Sphere(center::SVector{3, Float64}, radius::Float64)
    Cube(center::SVector{3, Float64}, side_length::Float64)
end

function intersect(p::Primitive, r::Ray)
    @cases p begin
        Sphere(center, radius) => ...
        Cube(center, side_length) => ...
    end
end

```

The (perhaps confusingly named) package [DynamicSumTypes.jl](https://github.com/JuliaDynamics/DynamicSumTypes.jl/) might also be an even better fit here, I think you’d just write

```julia
using DynamicSumTypes

struct Sphere # regular julia type!
    center::SVector{3, Float64}
    radius::Float64
end
struct Cube # regular julia type!
    enter::SVector{3, Float64}
    radius::Float64
end

# sum-type that can contain either a Sphere or a Cube
@sumtype Primitive(Sphere, Cube)

# if you get a Primitive, unpack it and dispatch on the result
intersect(p::Primitive, r::Ray) = intersect(variant(p), r) 

# regular dispatch!
intersect(s::Sphere, r::Ray) = ...
intersect(s::Cube, r::Ray) = ... 

```

---

<div class="post-metadata">

**Author:** ![sdanisch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sdanisch/32/1406_2.png) [@sdanisch](https://discourse.julialang.org/u/sdanisch)\
**Post date:** [September 5, 2024, 4:56pm UTC](https://discourse.julialang.org/t/what-are-the-dangers-of-eval-in-macros-to-get-the-values-of-an-enum-symbol/119078/3 "2024-09-05T16:56:26Z")

</div>

> [@NatMath](#):
>
> entation of the PBRT 4 raytracer in Juli

Btw, I’m putting quite a bit of time into making Trace.jl perform well and run on the GPU:

> **[GitHub - pxl-th/Trace.jl: Physically-based ray tracing on CPU](https://github.com/pxl-th/Trace.jl)**
>
> Physically-based ray tracing on CPU

Which is also based on PBRT.

I solved some of those problems, by also creating my own “uber” types basically.  
Maybe it will be nice to switch to DynamicSumTypes at some point.

I’m still looking into how to efficiently iterate over different primitive types, but simply converting everything to a triangle mesh works pretty well for now.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [September 6, 2024, 6:22pm UTC](https://discourse.julialang.org/t/what-are-the-dangers-of-eval-in-macros-to-get-the-values-of-an-enum-symbol/119078/4 "2024-09-06T18:22:28Z")

</div>

Just to answer your original question, one bug (albeit fixable) is that `eval(expr)` in a macro evaluates in the module the macro was defined, which is probably not what you wanted.

The core philosophical reason to avoid this is that you generally want to delay evaluation until the code _is actually run_ — and thus macros should just do what they need to in order to spit out the expressions that’ll run later. Muddling what get executed when is just a cause for confusion in many cases.

This case, though, is more comparable to a replacement for a metaprogramming loop like:

```julia
for v in instances(Shape)
    fname = Symbol(:intersect_, v)
    @eval $fname(arg) = ...
end

```

Which, yeah, uses `@eval` and is perhaps the most idiomatic way to ever use eval. Even if you’re doing this with lots of enums, you can just add another outer for loop. It’s a pretty common pattern that most folks will immediately identify and understand — and Revise.jl can understand it, too! I don’t think the macro-generated definitions are typically Revise-able.

I suppose if you _really_ wanted to use a macro here you could — and then one step better would be to `getfield( __module__ , enum)` to resolve the name in the correct scope instead doing a whole eval.
