How to make macro that calls expr asyncly?

let’s say you have a function (in the Main scope):

function foo()
  for cur_index in 99:-1:0
    print("$(cur_index) bottles of beer on the wall. ")
    println("$(cur_index) bottles of beer.")
    println("Take one down. Pass it around.")
    sleep(0.5)
  end
end

and you want to run another function at the same time through some macro buzz (described later):

module Fizz

  function bar()
    cows_come_home = false
    @async while !cows_come_home
      cur_rand = rand(0:99)
      cows_come_home = ( cur_rand < 5 )

      println("$(cur_rand) bottles of beer!")
      sleep(cur_rand/100)
    end
  end

  # macro buzz defined below
  # (just a wrapper for bar func)

  export @buzz

end

if you wanted to create a macro, that:

  • calls bar no matter what and
  • calls some expression async-ly

How would you do it?

The following macro gives an undefined error:

module Fizz

  # function bar defined above
  # (just wrapped by buzz)

  macro buzz(cur_expr::Expr)
    bar()

    cur_expression = Expr(
      :macrocall,
      Symbol("@async"),
      cur_expr
    )

    return cur_expression
  end

  export @buzz

end

when making the call:

julia> using Fizz
julia> @buzz foo()

20 bottles of beer!ERROR (unhandled task failure): UndefVarError: foo not defined
Stacktrace:
 [1] (::##1#2)() at ./task.jl:335

Task (failed) @0x00000001246d3610
UndefVarError: foo not defined
(::##1#2)() at ./task.jl:335

julia> 74 bottles of beer!
2 bottles of beer!

You’re quite close, but you need to change two things:

  1. You probably don’t want to call bar() inside the body of the macro, since that will cause bar() to be called when the macro is expanded instead of where it occurs in the code. Just put the call inside the returned expression.
  2. You need to escape cur_expr since otherwise it will be sanitized by the macro hygiene system. User input must be esced exactly once.
module Fizz

function bar()
    cows_come_home = false
    @async while !cows_come_home
      cur_rand = rand(0:99)
      cows_come_home = ( cur_rand < 5 )

      println("$(cur_rand) bottles of beer!")
      sleep(cur_rand/100)
    end
  end

macro buzz(expr::Expr)
    quote
        bar()
        $(Expr(:macrocall,
            Symbol("@async"),
            esc(expr)
        ))
    end
end

end

usage:

Main> Fizz.@buzz foo()
11 bottles of beer!99 bottles of beer on the wall.
Task (runnable) @0x00007faefc172230
99 bottles of beer.

Main> Take one down. Pass it around.
Main> 20 bottles of beer!
13 bottles of beer!
56 bottles of beer!
98 bottles of beer on the wall. 98 bottles of beer.
Take one down. Pass it around.
<truncated>
2 Likes

lol. that’s too fun :stuck_out_tongue: