Macro design recommendations for `do-end` like semantics

In Julia, there’s a function do block syntax like so:

foo() do x
  println(x)
end

where foo is defined like foo(f::Function)

I’m interested in writing a macro that affects a block. i.e. I want user facing code to look something like this:

foo() do x
  println(x)
end

but where foo is defined like this instead: foo(expr::Expr)

I’ve tried writing a macro but this following example doesn’t work (because @foo() is evaluated first):

julia> @foo() function(x)
  println(x)
end

ERROR: syntax: extra token "function" after end of expression
Stacktrace:
 [1] top-level scope
   @ none:1

And this would work but it is easy for users to accidentally remove the space between @foo and () and that leads to the previous structure which throws an error.

@foo () function(x)
  println(x)
end

I’m curious if anyone has suggestions for alternative designs here?

It’s not so clear to me what you want, but macros can certainly take do blocks:

julia> macro foo(exs...)
         @show exs; nothing
       end;

julia> @foo(y, 2, z=3) do x
         g(x)
       end
exs = (:((x,)->begin
          #= REPL[138]:2 =#
          g(x)
      end), :y, 2, :(z = 3))
3 Likes

Ah nice! I thought I tried this but must have messed up the number of arguments and failed to see the dispatch error. That helps! Thanks!!