# Continuation passing style and compile-time computation

**URL:** <https://discourse.julialang.org/t/continuation-passing-style-and-compile-time-computation/98769>\
**Category:** General Usage\
**Created:** [May 12, 2023, 9:52pm UTC](https://discourse.julialang.org/t/continuation-passing-style-and-compile-time-computation/98769 "2023-05-12T21:52:41Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![olynch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/olynch/32/17839_2.png) [@olynch](https://discourse.julialang.org/u/olynch)\
**Post date:** [May 12, 2023, 9:52pm UTC](https://discourse.julialang.org/t/continuation-passing-style-and-compile-time-computation/98769/1 "2023-05-12T21:52:41Z")

</div>

n AlgebraicJulia, we have a repeated problem that shows up in a number of places, where we want to pass values computed by one macro into another macro.

One classic example of this sort of problem is that we often want to generate struct definitions. But the “spec” for those structs might be generated compositionally, i.e. we don’t want to write down the entire spec in the body of the macro.

The problem is, macros only get access to the syntax that you pass in, so there’s no way to pass in values… right?

The way we currently do this is with `eval`. But calling `eval` on a struct definition seems icky.

I recently thought of another way. The only way to pass values into the “compile time context” (i.e. the context of the expander) is by defining macros. So… to output the result of a macro, we simply expand into a macro definition. But we can’t just pass in a macro as an argument to another macro, because the outer macro expands first. So we use a little trick based on continuation passing.

The idea is this.

We start out with a macro call `@f(@a, 3)`. We this expands into a macro call `@a(@f_internal(3))`. Then this expands into a macro call `@f_internal(4, 3)`. Basically, `@f` takes its first argument, and passes “what to do with its value” to it. Then `@a` takes in an expression, and inserts its value into the first argument. Then finally, `@f_internal` runs, and it has access to the value inserted in by `@a`.

This is kind of janky, because you have to trust that `@a(@f_internal(3))` will actually just expand into `@f_internal(4, 3)` or whatever. But `@a` could be an arbitrary macro, so this could not do at all what you expected. So I don’t know how practical this is to actually use. But it does show how you can inject things into the compile time context, if all you’ve got is the ability to define macros.

I would much prefer to have proper compile-time variables, ala Zig, but I doubt those will be added to Julia any time soon…

Here’s a toy implementation of this pattern: [Macrology.jl · GitHub](https://gist.github.com/olynch/72109e5d9d35fd293ab68fd5f60c64f2)

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [May 12, 2023, 11:56pm UTC](https://discourse.julialang.org/t/continuation-passing-style-and-compile-time-computation/98769/2 "2023-05-12T23:56:09Z")

</div>

> [@olynch](#):
>
> `@f (@a, 3)`

If `@f` knows about `@a`, it can just rewrite this to call the macro expansion in `@a`. It doesn’t need `@eval`.

For example:

```julia
a(expr) = ...transform expr...
macro a(expr)
    return a(expr)
end
macro f(expr)
    if Meta.isexpr(expr, :macrocall) && expr.args[1] == Symbol("@a")
        expr = a(expr.args[end]) # expand @a macro call
    end
    return ...some transformation of expr...
end

```

---

<div class="post-metadata">

**Author:** ![olynch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/olynch/32/17839_2.png) [@olynch](https://discourse.julialang.org/u/olynch)\
**Post date:** [May 15, 2023, 4:41pm UTC](https://discourse.julialang.org/t/continuation-passing-style-and-compile-time-computation/98769/3 "2023-05-15T16:41:47Z")

</div>

Ah, but the whole point is that `@a` is generated by a previous macro call. So `f` doesn’t know about `@a` until macro expansion time.

---

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [May 15, 2023, 9:16pm UTC](https://discourse.julialang.org/t/continuation-passing-style-and-compile-time-computation/98769/4 "2023-05-15T21:16:42Z")

</div>

> [@olynch](#):
>
> But we can’t just pass in a macro as an argument to another macro, because the outer macro expands first.

I’m not sure I completely understand your problem, but if you want the inner macro to expand first, nothing prevents you to manually trigger the macro-expansion. To expand on your “Macrology” gist:

```julia
julia> macro add(x,y)
         x+y
       end
@add (macro with 1 method)

julia> @macroexpand @add 1 2
3

julia> macro x() 9 end
@x (macro with 1 method)

# Does not work because @x is not expanded first
julia> @macroexpand @add (@x) 3
ERROR: MethodError: no method matching +(::Expr, ::Int64)

```

```julia
julia> macro add2(x, y)
          # Explicitly trigger the expansion of x first
          macroexpand( __module__ , x) + y
       end
@add2 (macro with 1 method)

julia> @macroexpand @add2 (@x) 3
12

```

  

EDIT: here is a real-life example where the `@timeit` macro (from `TimerOutputs.jl`) explicitly macroexpands the expression on which it is called:

> <https://github.com/KristofferC/TimerOutputs.jl/blob/89f585e8f093e5a326c6f811b01359f6b1f19151/src/TimerOutput.jl#L262>

---

<div class="post-metadata">

**Author:** ![olynch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/olynch/32/17839_2.png) [@olynch](https://discourse.julialang.org/u/olynch)\
**Post date:** [May 24, 2023, 8:14pm UTC](https://discourse.julialang.org/t/continuation-passing-style-and-compile-time-computation/98769/5 "2023-05-24T20:14:56Z")

</div>

Ohhh I didn’t know I was looking for this, but this is exactly what I was looking for! Thanks @ffevotte!
