I want to define a macro within a function, that uses an input argument of the function:
function runModel(; model::CoronaModel, n=10, simDuration=2)
function $(esc(name))(args...; kwargs...)
$model.$(esc(name))($model, args... ; kwargs...)
I currently use this as a workaround,
# global scope
function $(esc(name))(args...; kwargs...)
$(esc(:(model.$name)))($(esc(:model)), args... ; kwargs...)
To be clear, in your example, the macro is supposed to create a function like the following?
function nextSickness(args...; kwargs...)
model.nextSickness(model, args; kwargs...)
I dare say it is never necessary to declare a macro inside a function (and certainly also never a good idea).
In this case, you can do it with a function:
function model_closure(model, symbol)
(args...; kwargs...) -> getproperty(model, symbol)(model, args...; kwargs...)
You probably know you can just do the following, but I’m guessing it’s somehow inconvenient in the real context?
nextSickness(args...; kwargs...) = model.nextSickness(model, args...; kwargs...)
I saw your other post on getting OOP-like functionality in Julia. The price you pay in headaches is steep when you go against a language’s major paradigms. Turn back while you still can!
I know I could do
nextSickness(args...; kwargs...) = model.nextSickness(model, args...; kwargs...), but I dislike unnecessary repetition. With my macro, I can do just
@injectModel nextSickness; It’s so elegant. One of the reasons I love metaprogramming. I am new to Julia, and have not even read the metaprogramming docs, so I thought perhaps some way existed to achieve this inner macro. To rephrase my purpose, this was more of a Julia-learning question rather than a practical problem. Also, if the metaprogramming capabilities should be high enough, any paradigm will be expressible in the language, and one would not need to change one’s natural thinking patterns. I think Julia’s architecture around weak OOP is superior for libraries, but for quick, dirty scripts, classic OOP can be empowering.
PS: Going against the flow seems like a good way to learn the subtleties of the language My favorite hack of the day is this little gem I found on the net:
macro copycode(name, definition)
# @def insertme some_code...
# @insertme => would insert some_code... here
# from http://www.stochasticlifestyle.com/type-dispatch-design-post-object-oriented-programming-julia/
It’s the most beautiful iteration of the copy-paste paradigm I have seen:)) Simply graceful.
If this is a Julia learning exercise, then I think the important lesson to take away is that this cannot be done. You cannot define a macro within a function and then use it within the same function. That’s just not how the language works. Macros are expanded before the function is run. If the macro is defined inside the function, then by definition it cannot exist before the function is run. Thus what you are asking for is not possible.
Fortunately, it’s also not necessary. There’s no reason for your macro to be defined inside the function at all. If you just want:
to be transformed into
nextSickness(...) = model.nextSickness(...)
then there is no reason for the macro definition to live inside the function. Just write the macro outside and use it within the function.
I see. If this is all a game, I’m happy to play along
You can get essentially the OOP feel in julia, it’s just a bad idea. Note that I don’t condone the following
function Base.getproperty(ms::MyStruct, s::Symbol)
f = getfield(ms, s)
f isa Function ? x->f(ms, x) : f
julia> s = MyStruct(1, (c, x) -> c.a + x);
What’s the best way to structure a script that has some initial parameters (in a mathematical sense, not parametrized types) that stay constant during its run? I currently use a wrapper function that accepts the initial params, and define the functions that depend on these params as inner functions in this big wrapper function. That’s why I am trying to define macros in a function. Using the global scope directly, without a wrapper function, will cause performance problems, won’t it? Because the compiler can’t optimize on global variables (which the initial params would then be.). (Using
const definitions make the REPL workflow impossible.)
PS: I did say that I am using a global macro as a workaround in my question. It works, but I have hardcoded the symbol
:model in it, which felt a bit unclean at first. Now that I think about it, it doesn’t seem very problematic.
Don’t write scripts. Write functions, and pass parameters as arguments rather than using global data.
And also look at Parameters.jl, which was pretty much designed for this use case.
Common Lisp allowed you to do it, though of course it couldn’t use the local variables, just the “local syntax”. It might seem pointless, but it was very useful in fancier macros. You could have
@with_dataframe_columns df begin
@set EyeColor .= :blue
@with_dataframe_columns would define the local macros
@set, which use
df in their expansion. You can accomplish the same in Julia, but it’s a lot uglier.