# Macro defining an internal macro?

**URL:** https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700
**Category:** General Usage
**Tags:** macros
**Created:** [May 24, 2021, 1:42am UTC](https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700 "2021-05-24T01:42:40Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![Mark\_Nahabedian](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mark_nahabedian/32/16562_2.png) [@Mark\_Nahabedian](https://discourse.julialang.org/u/Mark_Nahabedian)
#### Post date: [May 24, 2021, 1:42am UTC](https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700/1 "2021-05-24T01:42:40Z")

</div>

I need some help with macros.

I have a directed graph that is represented by a collection of Pairs where each Pair represents an arc of the graph.

I want to perform some transformations on the graph. Each transformation function will be called on each :first of each Pair, perform some tests (querying the graph in the process), and, if none of those tests fail, will return two collections of Pairs. The first collection is of arcs that should be added to the graph, and the second of arcs to be removed.

To simplify the writing of these transformation functions, I want a macro that provides three operations:

1. check(condition): to evaluate a condition and return from the function if the condition is not met;

2. add(arc): add an arc to the first return value (arcs to be added to the graph);

3. remove(arc): add an arc to the second return value (arcs to be removed from the graph).

add and remove can be implemented as internal functions, but I don’t think check can since it needs to return from an outer context.

When I try to define the check macro within the macro that provides this DSL to the body of a transformation function though. I get an error::

```julia
using Markdown
import MacroTools

macro graphTransformerBody(bodyblock)
  quote
    let
      addset = Set()
      removeset = Set()
          add(x) = push!(addset, x)
          remove(x) = push!(removeset, x)

          macro check(condition)
            quote
              #=
              if ! $condition
                return addset, removeset
              end
              =#
            end
          end

          $bodyblock
      return addset, removeset
    end
  end
end

show(MacroTools.prewalk(MacroTools.rmlines, 
@macroexpand( @graphTransformerBody(let
  @check(foobar)
  add(x)
end)
        )))

```

produces

```julia
quote
    let
        var"#1#addset" = Main.Set()
        var"#2#removeset" = Main.Set()
        var"#3#add"(var"#9#x") = begin
                Main.push!(var"#1#addset", var"#9#x")
            end
        var"#4#remove"(var"#10#x") = begin
                Main.push!(var"#2#removeset", var"#10#x")
            end
        macro Main.check(var"#11#condition")
            $(Expr(:copyast, :($(QuoteNode(quote
    #= c:\Users\Mark Nahabedian\.julia\dev\PanelCutting\src\macrotest.jl:19 =#
end)))))
        end
        let
            var"#3#add"(Main.x)
        end
        return (var"#1#addset", var"#2#removeset")
    end
end

```

as I expect.

If I uncomment the invocation of '@check` thouh, I get  
the error

```julia
ERROR: LoadError: LoadError: UndefVarError: @check not defined

```

Any suggestions for how I can get this to wokr?

Thanks.

---

<div class="post-metadata">

### Author: ![jzr](https://avatars.discourse-cdn.com/v4/letter/j/eb9ed0/32.png) [@jzr](https://discourse.julialang.org/u/jzr)
#### Post date: [May 24, 2021, 2:08am UTC](https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700/2 "2021-05-24T02:08:36Z")

</div>

I don’t know if this is the issue you’re facing but there has been some discussion that under the current design, macros defining macros are hard to use.

[https://github.com/JuliaLang/julia/issues/37691](https://github.com/JuliaLang/julia/issues/37691)

[https://github.com/JuliaLang/julia/pull/6910](https://github.com/JuliaLang/julia/pull/6910)

---

<div class="post-metadata">

### Author: ![jameson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jameson/32/23_2.png) [@jameson](https://discourse.julialang.org/u/jameson)
#### Post date: [May 24, 2021, 2:37am UTC](https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700/3 "2021-05-24T02:37:16Z")

</div>

> [@Mark\_Nahabedian](#):
>
> `bodyblock`

You’re just missing `$(esc(bodyblock))`. A general rule of thumb is that all arguments need to be wrapped in `esc`, and nothing else should be. There are exceptions, but that is usually good enough place to begin.

---

<div class="post-metadata">

### Author: ![jameson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jameson/32/23_2.png) [@jameson](https://discourse.julialang.org/u/jameson)
#### Post date: [May 24, 2021, 2:44am UTC](https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700/4 "2021-05-24T02:44:52Z")

</div>

Oh, sorry, I misread this as a hygiene question. It is a phasing question instead however. You are not defining an inner macro in this macro, you are defining _how_ to define an inner macro with this macro. But that sequencing implies that the new macro won’t be available.

To do this, you’d instead write a pre-walk pass that pre-parses bodyblock, and calls a function on it.

```julia
macro graphTransformerBody(bodyblock)
    addset = Set()
    removeset = Set()
    add(x) = push!(addset, x)
    remove(x) = push!(removeset, x)
    check(x) = condition ? return (addset, removeset) : nothing
    bodyblock = preprocess!(bodyblock, check, :macrocall, sym"@check") # user implemented function taht walks over bodyblock AST and calls `check` on every Expr that matches this :macrocall
    return quote
        $(esc(bodyblock))
        return addset, removeset # N.B. the return keyword here is very unusual
    end
end

```

---

<div class="post-metadata">

### Author: ![Mark\_Nahabedian](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mark_nahabedian/32/16562_2.png) [@Mark\_Nahabedian](https://discourse.julialang.org/u/Mark_Nahabedian)
#### Post date: [May 24, 2021, 5:51am UTC](https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700/5 "2021-05-24T05:51:26Z")

</div>

Thanks Jameson and jzr.

I thought walking code was what evaluators and compilers were fpr 🙂

Where does preprocess! come from? I can’t find documentation for it (julia 1.6.0).

---

<div class="post-metadata">

### Author: ![jameson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jameson/32/23_2.png) [@jameson](https://discourse.julialang.org/u/jameson)
#### Post date: [May 24, 2021, 4:48pm UTC](https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700/6 "2021-05-24T16:48:58Z")

</div>

You might need to scroll above to read the comment: this is a function you need to implement. Yes, compilers also analyze the code, but here you are trying to intercept that to do something different, so you need to implement that something different.

---

<div class="post-metadata">

### Author: ![Mark\_Nahabedian](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mark_nahabedian/32/16562_2.png) [@Mark\_Nahabedian](https://discourse.julialang.org/u/Mark_Nahabedian)
#### Post date: [May 25, 2021, 2:52pm UTC](https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700/7 "2021-05-25T14:52:18Z")

</div>

Thanks Jameson. That comment didn’t sink in at 2am when I first read it but when I reread it yesterday morning it did.

Having to do that seems way to fraught to me. I’m giving up on using nested macros. I’m implementing `check` as a local function that throws to a catch handler to do its non-local exit. I’m also doing my own macro hyhiene since the regular mechanism was getting in the way. Here’s my new code:

```julia
struct NonlocalTransfer <: Exception
  uid

  function NonlocalTransfer()
    new(UUIDs.uuid1())
  end
end

function Base.showerror(io::IO, e::NonlocalTransfer)
  print(io, "NonlocalTransfer not caught!")
end

macro graphTransformerBody(bodyblock)
      exittag = gensym("exittag")
      addset = gensym("addset")
      removeset = gensym("removeset")
  esc(quote
    let
      $exittag = NonlocalTransfer()
      $addset = Set()
      $removeset = Set()
      add(x) = push!($addset, x)
      remove(x) = push!($removeset, x)
      function check(condition)
        if !condition
          throw($exittag)
        end
      end
      try
        $bodyblock
      catch e
        if e != $exittag
          rethrow(e)
        end
      end
      return $addset, $removeset
    end
      end)
end

```

---

<div class="post-metadata">

### Author: ![yha](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yha/32/3502_2.png) [@yha](https://discourse.julialang.org/u/yha)
#### Post date: [May 25, 2021, 3:18pm UTC](https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700/8 "2021-05-25T15:18:06Z")

</div>

Does this problem really need a macro? How about something like this:

```julia
function graph_transform(f)
    exittag = NonlocalTransfer()
    addset = Set()
    removeset = Set()
    add(x) = push!(addset, x)
    remove(x) = push!(removeset, x)
    check(condition) = condition || throw(exittag)
    try
        f(add, remove, check)
    catch e
        e == exittag || rethrow(e)
    end
    return addset, removeset
end

graph_transform() do add, remove, check
   # code block
end

```

---

<div class="post-metadata">

### Author: ![jameson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jameson/32/23_2.png) [@jameson](https://discourse.julialang.org/u/jameson)
#### Post date: [May 25, 2021, 3:26pm UTC](https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700/9 "2021-05-25T15:26:25Z")

</div>

> [@Mark\_Nahabedian](#):
>
> doing my own macro hyhiene

Looks about right. Note that you forgot hygiene on a few symbols though (NonlocalTransfer, Set, push!, !, throw, !=, rethrow) which should all be `GlobalRef(@ __MODULE__ , :Set)`, etc.

Alternatively, add `esc()` around `bodyblock`, `add`, `remove`, and `check` and you shouldn’t need to add mangling to anything else.

---

<div class="post-metadata">

### Author: ![Mark\_Nahabedian](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mark_nahabedian/32/16562_2.png) [@Mark\_Nahabedian](https://discourse.julialang.org/u/Mark_Nahabedian)
#### Post date: [May 25, 2021, 5:38pm UTC](https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700/10 "2021-05-25T17:38:41Z")

</div>

Thanks Jameson.

---

<div class="post-metadata">

### Author: ![Mark\_Nahabedian](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mark_nahabedian/32/16562_2.png) [@Mark\_Nahabedian](https://discourse.julialang.org/u/Mark_Nahabedian)
#### Post date: [May 25, 2021, 5:41pm UTC](https://discourse.julialang.org/t/macro-defining-an-internal-macro/61700/11 "2021-05-25T17:41:45Z")

</div>

Thanks. Very elegant.

It’s been decades since I’ve read any of Steele’s “Lambda the Ultimate …” papers. It may be time to reread them and reset my head.
