What’s the idiomatic and DRY way of writing something like this:
function foo(async::Bool)
if async
@async dostuff()
else
dostuff()
end
The problem here is that the code is not DRY, I have to call dostuff() twice (and the problem is, in general, there will be more logic duplicated).
I tried referencing the macro but it errors out (syntax error) - in the line of:
function foo(async::Bool)
doasync = async ? @async : @sync # errors out
doasync dostuff()
end
Any idea? It’s a pretty common pattern for me, for example, I have many instances where if a debug flag is set, certain computations are prefixed with @time. Now it’s the same non-DRY mess.
macro maybeasync(asyncflag, expr)
quote
if $(esc(asyncflag))
@async $(esc(expr))
else
$(esc(expr))
end
end
end
function docos(flag)
@maybeasync flag cos(1)
end
If this is the only way to go, I guess it should be something at one level of abstraction higher, to pass the macro, the flag, and the expression (so that it will work with @async but also with @time and others).
And again, I guess one of the issues will be that macros can’t be passed around (referenced) as arguments (or at least I don’t know how).
Yeah. I thought the same thing about a more general macro. This is probably already implemented in a few utility files scattered around github…
macro maybemacro(asyncflag, themacro, expr)
mccall = Expr(:macrocall, Symbol("@", themacro), LineNumberNode(@__LINE__), expr)
quote
if $(esc(asyncflag))
$mccall
else
$(esc(expr))
end
end
end
Thanks so much, @jlapeyre and @Tamas_Papp - I’ll give the two approaches a try today, to see how they work.
They both look great although in the general case I believe that the macro approach could be more efficient as it won’t evaluate the input on every function call. Similar to Metaprogramming · The Julia Language
@Mason first-class macros would be awesome. It looks like lisp-style macros could be “upgraded” to first-class macros. I’d love to see that in Julia.
Sorry, I don’t think I properly articulated what was going through my head.
I have these global (environment) variables which are set upon application initialisation and don’t change throughout the lifetime of the script. Things like DEBUG = true | false, ENV = dev | test | prod, etc. These affect whether or not subsequent macros like @time, @async, etc are used (ie if ENV == dev then many statements are prefixed by the @time macro; but in prod they are not).
I was thinking that with macros I can remove the IF evaluation altogether and just use the corresponding branch throughout the lifetime of the script. So the IF/ELSE is evaluated at parse time and the resulting expression is used from that point on. Similar to the parse time vs run time example here: Metaprogramming · The Julia Language
Interesting - wondering how does that work, given that Julia allows changing the value of the const? I presume the function will be recompiled when that happens?
Oh, yes, that’s a good point. They’re not actually constants but fields in a config object. I could se tup some key constants, like const DEBUG = config.debug. Or maybe just refactor the config type to be immutable - that should have a similar effect as the constants, I expect.
I am not sure (though it may not be “undefined” and just happens to work below).
julia> VERSION
v"1.2.0-DEV.28"
julia> const A = 1
1
julia> f() = A
f (generic function with 1 method)
julia> const A = 2
WARNING: redefining constant A
2
julia> f()
2
I was actually recently trying to figure out this exact case of making @threads optional, based on a flag passed to a function. Would the macro you’re proposing be applicable for something like the following?
function func(args...; use_threads=false)
@maybethread use_threads for i = 1:10
do_something_with(i, args...)
end
end
If not, is there a macro that would work for this kind of use case?
i don’t know how to pass in a variable without duplicating the loop code-gen. so instead, i’m now testing an alternative where a global use_threads is defined.
in my case, the loop is actually bit slower if i haven’t started julia with the -t flag, so i also check for that and skip the @threads macro if there is just one thread.
macro maybethread(loop)
if use_threads && Threads.nthreads()>1
return quote Threads.@threads $loop; end
else
return quote $loop; end
end
end