Macro definition throws MethodError that doesn't make sense

question
metaprogramming

#1

Hello,

I’m trying to define a macro which will evaluate an expression using @sync @parallel if the Boolean argument passed in is true. When I define this macro in the REPL, it seems to work fine, but it throws a mysterious MethodError at compile-time when I define it within a module.

I define TestModule in the file test_module.jl:

module TestModule

macro mypar(parallel::Bool, ex::Expr)
    return :( $(esc(parallel)) ? (@sync @parallel $(esc(ex))) : $(esc(ex)) )
end

function myfun(parallel::Bool)
    @mypar parallel for i = 1:10
        sleep(rand())
        @show i
    end
end

end

Then I get:

julia> include("test_module.jl")
ERROR: LoadError: MethodError: no method matching @mypar(::Symbol, ::Expr)
Closest candidates are:
  @mypar(::Bool, ::Expr) at /home/pearl/test_module.jl:4
Stacktrace:
 [1] include_from_node1(::String) at ./loading.jl:569
 [2] include(::String) at ./sysimg.jl:14
while loading /home/pearl/test_module.jl, in expression starting on line 7

However, when I change the first line of myfun to @mypar true for i = 10, it compiles successfully. What am I doing incorrectly here?


#2

Macros are expanded before the code is run, which makes them different from functions. parallel is a symbol, not a literal Bool here.


#3

@kristoffer.carlsson What should I be doing instead? I’ve been looking at the examples in the Julia documentation and Wikibooks, and I’m not sure I understand the difference between @mypar and the examples in those. I know you can use macros in compiled code - for example, @time works just fine. I thought that this was accomplished by escaping parallel in my @par definition, so it wouldn’t be evaluated until run-time, but it doesn’t seem to be getting into the @mypar body at all, since it throws the MethodError.


#4

You can think of macros as a function that takes syntax and output new syntax.

Dropping the ::Bool constraint might be enough here.


#5

It was! Thank you!