# Shared Variables In Nested Macros

**URL:** https://discourse.julialang.org/t/shared-variables-in-nested-macros/62821
**Category:** General Usage
**Tags:** question, macros
**Created:** [June 13, 2021, 1:45am UTC](https://discourse.julialang.org/t/shared-variables-in-nested-macros/62821 "2021-06-13T01:45:19Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![Acmion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/acmion/32/26051_2.png) [@Acmion](https://discourse.julialang.org/u/Acmion)
#### Post date: [June 13, 2021, 1:45am UTC](https://discourse.julialang.org/t/shared-variables-in-nested-macros/62821/1 "2021-06-13T01:45:19Z")

</div>

I’d like to share variables between nested macros in a convenient manner, but this seems to be difficult. I will try to illustrate the situation with some code examples.

To create custom properties to Julia types one can do the following:

```julia
struct S 
    field
end

function Base.getproperty(this::S, sym::Symbol)
    if sym === Symbol("property")
        return this.field * 2
    end

    if sym === Symbol("fun")
        return function(n::Int)
            return this.field * n
        end
    end

    return getfield(this, sym)
end

s = S(10)
s.field
s.property
s.fun(3)

```

However, the branching `Base.getproperty` is not too pretty. What I’d like instead is the following (which should have the same behavior as above):

```julia
@defs S begin
    @def property this.field * 2
    
    @def fun function(n::Int)
        return this.field * n
    end
end

```

This looks significantly better, but this requires sharing of variables between macros:

```julia
module PropertyMacros
    export @defs, @def

    macro defs(type, body)
        type = esc(type)
        body = esc(body)

        return quote
            function Base.getproperty(this::$type, sym::Symbol)
                $body
                return getfield(this, sym)
            end
        end
    end

    macro def(symbol, body)
        symb = string(symb)
        body = esc(body)

        return quote

            # NOTE: "sym" has been defined in defs !
            if sym === Symbol($symb)
                return $body
            end
        end
    end
end

```

Great, this seems to be what I want, however, there is one problem. The `def` macro does not  
know about `sym` and will always convert it to `PropertyMacros.sym` and thus breaking the  
functionality.

Using `@macroexpand` on the “pretty” code gives the following (I cleaned the generated comments):

```julia
using PropertyMacros

@macroexpand @defs S begin
    @def property this.field * 2
    
    @def fun function(n::Int)
        return this.field * n
    end
end

```

=\>

```julia
# NOTE: 
# The function signature defines: var"#8#sym"
# However, the variable used in the ifs is: Main.PropertyMacros.sym

quote
    function (Main.PropertyMacros.Base).getproperty(var"#7#this"::S, var"#8#sym"::Main.PropertyMacros.Symbol)
        begin
            begin
                if Main.PropertyMacros.sym === Main.PropertyMacros.Symbol("property")
                    return this.field * 2
                end
            end
            begin
                if Main.PropertyMacros.sym === Main.PropertyMacros.Symbol("fun")
                    return function (n::Int,)
                        return this.field * n
                    end
                end
            end
        end
        return Main.PropertyMacros.getfield(var"#7#this", var"#8#sym")
    end
end

```

The problem with the generated code is that `var"#8#sym" != Main.PropertyMacros.sym` (same  
problem actually applies to the `this` identifier).

The easy solution to this problem would be to just tell the macro generator to not transform some identifiers or to just inject strings, like this fictional example: `injectstring("sym")`, but without the quotes.

Another solution would be to somehow specify macro dependencies, but this feature is unlikely implemented in Julia.

I did successfully implement this behavior using raw strings, `Meta.parse`, `esc` and `@eval`, but that code is quite ugly.

Is this problem solvable at the moment without using raw strings? Any help would be much appreciated!

---

<div class="post-metadata">

### Author: ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)
#### Post date: [June 13, 2021, 8:49am UTC](https://discourse.julialang.org/t/shared-variables-in-nested-macros/62821/2 "2021-06-13T08:49:27Z")

</div>

You’ll need to `esc` some of the argument names, `this`, and `sym`, which should probably reference the right things then:

```julia
macro defs(type, body)
    type = esc(type)
    body = esc(body)
    this = esc(:this)
    sym = esc(:sym)
    return quote
        function Base.getproperty(this::$type, sym::Symbol)
            $body
            return getfield(this, sym)
        end
    end
end

macro def(symbol, body)
    symb = string(symbol)
    body = esc(body)
    sym = esc(:sym)
    return quote

        # NOTE: "sym" has been defined in defs !
        if sym === Symbol($symb)
            return $body
        end
    end
end

```

If `@def` is never used outside of `@defs` then maybe don’t even define a `@def` macro and just do all the processing inside of `@defs` instead. That would save you some of the headaches of dealing with macro hygiene.

The `string` to `Symbol` conversions in `@def` can be switched to use `Meta.quot` btw:

```julia
macro def(symbol, body)
    body = esc(body)
    sym = esc(:sym)
    return quote
        # NOTE: "sym" has been defined in defs !
        if $sym === $(Meta.quot(symbol))
            return $body
        end
    end
end

```

---

<div class="post-metadata">

### Author: ![Acmion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/acmion/32/26051_2.png) [@Acmion](https://discourse.julialang.org/u/Acmion)
#### Post date: [June 13, 2021, 4:31pm UTC](https://discourse.julialang.org/t/shared-variables-in-nested-macros/62821/3 "2021-06-13T16:31:39Z")

</div>

Thank you very much!

You missed some interpolating `$` in you code, but otherwise it works perfectly!

This should be the final code, including the missing `$`:

```julia
module PropertyMacros
    export @defs, @def

    macro defs(type, body)
        type = esc(type)
        body = esc(body)
        this = esc(:this)
        sym = esc(:sym)
        return quote
            function Base.getproperty($this::$type, $sym::Symbol)
                $body
                return getfield(this, sym)
            end
        end
    end
    
    macro def(symbol, body)
        body = esc(body)
        sym = esc(:sym)
        return quote
            # NOTE: "sym" has been defined in defs !
            if $sym === $(Meta.quot(symbol))
                return $body
            end
        end
    end
end

```

And now this will work:

```julia
using PropertyMacros 

struct S 
    field
end

@defs S begin
    @def property this.field * 2
    
    @def fun function(n::Int)
        return this.field * n
    end
end

s = S(10)
s.field
s.property
s.fun(3)

```

=\>

```julia
10
20
30

```
