# Dispatch in macro?

**URL:** <https://discourse.julialang.org/t/dispatch-in-macro/34565>\
**Category:** General Usage\
**Created:** [February 13, 2020, 9:36am UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565 "2020-02-13T09:36:19Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [February 13, 2020, 9:36am UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/1 "2020-02-13T09:36:19Z")

</div>

suppose I have two custom types `A` and `B`, and I wanna build a macro or a generated function that gives different behaviors according to the type, how could I do?

I tried the following:

```julia
struct A
    data::Int
end
struct B
    data::Int
end
a = A(1);
b = B(2);

```

is it possible to have a macro `@f()` or generated function `f()` that behaves like:

```julia
@f(a, x) or f(a, x) or @f(A, a, x) or @f(Type{A}, a, x)
@f(b, x) or f(b, x) or @f(B, b, x) or @f(Type{B}, b, x)

```

that respectively produce expressions like:

```julia
:(a.data + x)
:(b.data * x)

```

?  
please advise, thanks 🙏

---

<div class="post-metadata">

**Author:** ![mohamed82008](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mohamed82008/32/18171_2.png) [@mohamed82008](https://discourse.julialang.org/u/mohamed82008)\
**Post date:** [February 13, 2020, 10:00am UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/2 "2020-02-13T10:00:30Z")

</div>

No need for a macro to do this, use a function with dispatch.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [February 13, 2020, 10:05am UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/3 "2020-02-13T10:05:51Z")

</div>

could you be nice enough to provide an example? 🙏  
noted that I need to produce an expression rather than a value. If a normal function is used, inside it I got only the values but lost the calling expressions (e.g. `a` and `x`)

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [February 13, 2020, 10:15am UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/4 "2020-02-13T10:15:01Z")

</div>

Maybe you want to have a [generated function](https://docs.julialang.org/en/v1/manual/metaprogramming/#Generated-functions-1)? But I’d also consider carefully whether a plain function can achieve what you want, metaprogramming should be used sparsely.

---

<div class="post-metadata">

**Author:** ![mohamed82008](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mohamed82008/32/18171_2.png) [@mohamed82008](https://discourse.julialang.org/u/mohamed82008)\
**Post date:** [February 13, 2020, 10:16am UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/5 "2020-02-13T10:16:30Z")

</div>

What are you going to do with the expression? Run it? Then just put it in a function.  
Example:

```julia
f(a::A, x) = a.data + x
f(b::B, x) = b.data * x

```

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [February 13, 2020, 10:44am UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/6 "2020-02-13T10:44:56Z")

</div>

no, I don’t want the value, I need the expression because I wanna build up nested expressions.  
e.g.

```julia
struct A
    data::Vector{Int}
end
struct B
    data::Vector{Int}
end

a = A([1, 2]);
b = B([3, 4]);

@f(b, @f(a, x) ) or @f(b, @f(a, :x) )

```

gives

```julia
:(b.data .* (a.data .+ x) )

```

I’m trying to build a nested expression for lazy evaluations.

---

<div class="post-metadata">

**Author:** ![mohamed82008](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mohamed82008/32/18171_2.png) [@mohamed82008](https://discourse.julialang.org/u/mohamed82008)\
**Post date:** [February 13, 2020, 10:59am UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/7 "2020-02-13T10:59:56Z")

</div>

How complicated will those expressions be? And is some code inside a package or something the user will play with? If there is only a small number of such expressions that need to be generated, and all the inputs can be made available to one function, use a normal function with an if statement on the types of the inputs to change the code being run. If an if statement is not enough and you need fancier transformations, use a generated function.

@cscherrer had a similar problem before and probably has some good advice on this kind of problem.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [February 13, 2020, 11:27am UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/8 "2020-02-13T11:27:35Z")

</div>

maybe doing with string is the only option? 🤔

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [February 13, 2020, 11:38am UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/9 "2020-02-13T11:38:32Z")

</div>

Strings are probably the worst option for working with ASTs.

I would suggest that you consider the clarifying questions that are asking for some context for your problem. Again,

1. it is very likely that you don’t need macros,
2. closures could help you build up expressions piecewise,
3. but since you provided no context, it is very difficult to help you in a more concrete way.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [February 13, 2020, 12:53pm UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/10 "2020-02-13T12:53:05Z")

</div>

> [@Tamas\_Papp](#):
>
> since you provided no context

let me clarify, given:

```julia
struct A
    data::Vector{Int}
end
struct B
    data::Vector{Int}
end
a = A([1, 2]);
b = B([3, 4]);

```

I would like to have a macro `@f()` that returns an **expression** so that:  
`@f(a, :x)` gives `:(a.data .+ x)`, and  
`@f(b, :x)` gives `:(b.data .* x)` (note the difference between `.+` and `.*`)

if it is possible, then I could create nested expression, e.g. by:  
`@f(a, @f(b, @f(a, :x) )`  
and get:  
`:(a.data .+ (b.data .* (a.data .+ x) ) )`

it’s difficult for me because:

1. as I need the **name** of the calling object (i.e. “a” in `@f(a, :x)` ), seems like I have to use a macro (not a function, not a generated function)
2. however, using macro I can **not** infer the type of the object yet at the same time I want the behavior of the macro be **different** according to the **type** of the object…

I hope that can be achieved… maybe by macro calling another macro? or using strings?

the problem is: how to define `@f()`? help please… thanks 🙇‍♂️

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [February 13, 2020, 1:43pm UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/11 "2020-02-13T13:43:20Z")

</div>

Sorry, this is not enough context for me, you are just repeating the original question.

This looks like an [XY problem](https://en.wikipedia.org/wiki/XY_problem) to me; hope someone else can help you.

---

<div class="post-metadata">

**Author:** ![kdyrhage](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kdyrhage/32/2326_2.png) [@kdyrhage](https://discourse.julialang.org/u/kdyrhage)\
**Post date:** [February 13, 2020, 1:49pm UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/12 "2020-02-13T13:49:31Z")

</div>

Is it something like this that you’re after?

```julia
macro f(s, x)
    ss = QuoteNode(s)
    x = QuoteNode(x)
    return quote
        if $s isa A
            :($$ss.data .+ $$x)
        elseif $s isa B
            :($$ss.data .* $$x)
        else
            :()
        end
    end
end

@f(a, x)

```

It doesn’t work with nested calls but maybe you can figure that out.

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [February 13, 2020, 1:59pm UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/13 "2020-02-13T13:59:43Z")

</div>

Just use a macro that calls another function

```julia
macro f(a, x)
    :(f($(esc(a)), $(QuoteNode(a)), $(esc(x)))
end

```

And no using strings never solve anything. You can do strictly less and make it more complicated by using strings for metaprogramming. You should stop thinking about strings as a solution to any metaprogramming problems.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [February 13, 2020, 3:20pm UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/14 "2020-02-13T15:20:05Z")

</div>

thanks @kdyrhage,

> [@kdyrhage](#):
>
> It doesn’t work with nested calls

in fact, the second argument should be expression, then I guess nested call would be possible, i.e.  
instead of calling:

```julia
@f(a, x)

```

we should call:

```julia
@f(a, :x)

```

could u please advise how to modify your macro such that `@f(a, :x)` gives `:(a.data .+ x)`?  
thanks.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [February 13, 2020, 3:29pm UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/15 "2020-02-13T15:29:50Z")

</div>

thanks @yuyichao!! 🙇‍♂️

it’s almost done:

```julia
macro f(a, x)
    :(g($(esc(a)), $(QuoteNode(a)), $(esc(x))) )
end

g(::A, sym::Symbol, ex::Union{Expr, Symbol}) =
    Expr(:call, :.+, Expr(:., sym, QuoteNode(:data) ), ex)
g(::B, sym::Symbol, ex::Union{Expr, Symbol}) =
    Expr(:call, :.*, Expr(:., sym, QuoteNode(:data) ), ex)

julia> @f(a, :y)
:(a.data .+ y)

julia> @f(b, @f(a, :y) )
:(b.data .* (a.data .+ y))

```

the only remaining problem is:

```julia
julia> @macroexpand @f(a, :y)
:(Main.g(a, :a, :y))

```

that calls `Main.g()`, how could I make it calling `g()` instead? thanks.

---

<div class="post-metadata">

**Author:** ![cscherrer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cscherrer/32/7631_2.png) [@cscherrer](https://discourse.julialang.org/u/cscherrer)\
**Post date:** [February 13, 2020, 3:29pm UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/16 "2020-02-13T15:29:53Z")

</div>

This is pretty close:

```julia
macro f(a, x)
    aname = QuoteNode(a)
    xname = QuoteNode(x)
    quote
        op = getop(typeof($a))
        Expr(:call, op, $aname, $xname)
    end
end

struct A
    data::Int
end

struct B
    data::Int
end

a = A(1);
b = B(2);

getop(::Type{A}) = +
getop(::Type{B}) = *

julia> @f a x
:((+)(a, x))

julia> @f b x
:((*)(b, x))

```

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [February 13, 2020, 3:40pm UTC](https://discourse.julialang.org/t/dispatch-in-macro/34565/17 "2020-02-13T15:40:27Z")

</div>

You don’t want to call `g`. You want to call `g` in the same module as `@f` so this is exactlty what you need.

In another word if you have

```julia
module A
macro f()
end
g() = ... // 1
end

g() = ... // 2
@f(...)

```

you want to call the `// 1` rather than `// 2`.
