# Using macros to modify function bodies

**URL:** https://discourse.julialang.org/t/using-macros-to-modify-function-bodies/61319
**Category:** General Usage
**Tags:** question, package
**Created:** [May 17, 2021, 8:17pm UTC](https://discourse.julialang.org/t/using-macros-to-modify-function-bodies/61319 "2021-05-17T20:17:39Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### Author: ![jmuchovej](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jmuchovej/32/21640_2.png) [@jmuchovej](https://discourse.julialang.org/u/jmuchovej)
#### Post date: [May 17, 2021, 8:17pm UTC](https://discourse.julialang.org/t/using-macros-to-modify-function-bodies/61319/1 "2021-05-17T20:17:40Z")

</div>

I’m coming from Python, attempting to build a Julia equivalent of [`huggingface/knockknock`](https://github.com/huggingface/knockknock). **Briefly:** KnockKnock decorates Python functions notifying you when said functions begin execution and fail/complete execution. My goals are approximately the same, just with Julia. (I realize this is attempting to use `@macro`s as `@decorator`s, and that they are distinct ideas with differing goals.)

## Questions

1. Can `@macro`s modify the function body, define the function, and return said function – but with the redefined body without needing to define a custom function? (One of my many attempts appears to fail at this.)
2. “Meta question”: Would achieving the above present capability issues with chaining macros? (From my own searching, I’ve vaguely grasped “yes”.)
  1. If so, would instead creating a syntax of… `@knockknock <messaging-client> func()` at-all sidestep this? (`<messaging-client>` would be something like `Telegram` or whatever else.)

## Attempting to produce a `@macro` that does (1)

The general problems I’ve run in to are the following:

1. KnockKnock-specific functions are executed when defining functions, instead of at run-time of the function.
2. When modifying the function definitions (i.e. `def[:body]`), I seem unable to actually “define” the function in Julia such that calling `foo(bar; baz=12)` actually works.

Of the many variants I’ve tried, I believe this snippet generally represents what I’m trying to do:

```julia
macro knockknock(func::Expr)
    def = MacroTools.splitdef(func)
    body = def[:body]

    def[:body] = quote
        # KnockKnock specific function, properly imported
        local t0 = setup(def)
        # Your function body (which is represented as a `quote ... end`)
        local retval = $body
        # KnockKnock specific function, properly imported
        local (t1, tt) = teardown(def, t0, retval)
        return retval
    end

   func = MacroTools.combinedef(def)
    quote
        $func
    end
end

```

Then using something equivalent to this:

```julia
@knockknock function test()
    println("sleeping...")
    sleep(10)
    println("...done.")
end

```

Running the above prints the output of `setup` and `teardown`. Then, running `test()` produces an `UndefVarError` (in the REPL is you’ll receive something like `#32#test (generic function with 1 method)`).

I’ve been basing much of this off the `@gen` macro from `Gen.jl`. Following it’s execution leads me to [this](https://github.com/probcomp/Gen.jl/blob/44f5f1408caf05ff95932bb58d9f57907c5084e3/src/dsl/dynamic.jl#L33-L61), which appears to use `quote ... end` to define the original function, then `Gen`’s `DynamicDSLFunction`. The main caveat here is that the `DynamicDSLFunction` needs to be run through `Gen`’s library functions which seem to use dispatch to call the `DynamicDSLFunction` variant (_which seems undesirable given my goals are to make usage require minimal changes to existing code (and not require knowing much about how the function is executed)_).

---

<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: [May 17, 2021, 9:16pm UTC](https://discourse.julialang.org/t/using-macros-to-modify-function-bodies/61319/2 "2021-05-17T21:16:52Z")

</div>

It doesn’t _exactly_ answer your macro questions, but have you considered just using closures instead to do this? Quite often they get you far enough that you don’t have to go messing with macros, though maybe I’ve overlooked something in the `knockknock` lib that requires that level of metaprogramming…

```julia
julia> setup(f) = nothing
setup (generic function with 1 method)

julia> teardown(f, t0, retval) = nothing, nothing
teardown (generic function with 1 method)

julia> function knockknock(f)
           function (args...; kws...)
               t0 = setup(f)
               retval = f(args...; kws...)
               t1, tt = teardown(f, t0, retval)
               return retval
           end
       end
knockknock (generic function with 1 method)

julia> f(x) = x + 1
f (generic function with 1 method)

julia> setup(::typeof(f)) = @show :before
setup (generic function with 2 methods)

julia> teardown(::typeof(f), t0, retval) = @show t0, retval
teardown (generic function with 2 methods)

julia> kkf = knockknock(f)
#1 (generic function with 1 method)

julia> kkf(1)
:before = :before
(t0, retval) = (:before, 2)
2

```

The `@show` calls in the `setup` and `teardown` definitions are there just for illustration, you can define whatever you what in those methods. The `::typeof(f)` dispatches _specifically_ on the type of the `f` function, if you’ve not come across that syntax before.
