In an attempt to make some numerical code (ie something thats basically
just a bunch of equations) more readable, I am trying to write a macro that
lets me write the code more succinctly. The code uses parameters from some
data structure, call it "mytype", so its littered with "t.a", "t.b", etc..
where t::mytype. My macro basically splices in the the "t." part for me.
Its kind of like how C++ member functions automatically access the class's
fields, as an example. To my amazement / growing love of Julia, I actually
managed to hack it together without too much difficulty, it looks like this,
macro self(func)
@assert func.head == :function
# add "self" as a first function argument
insert!(func.args[1].args,2,:(self::mytype))
# recurse through AST and rename X to self.X if
# its a fieldname of mytype
function visit(ex)
if typeof(ex) == Expr
ex.args = map(visit,ex.args)
elseif (typeof(ex) == Symbol) & (ex in fieldnames(mytype))
return :(self.$ex)
end
ex
end
func.args[2] = visit(func.args[2])
show(func) # print the edited function so we can see it in action
:($(esc(func)))
end
Here it is in action:
> @self function inc()
x = x + 1
end
:(function inc(self::mytype)
self.x = self.x + 1
end)
inc (generic function with 1 method)
> inc(mytype(0))
1
where I'm assuming I've defined mytype as
type mytype
x
end
As you can see, all it did was add self::mytype as an arg and replace x
with self.x everywhere it found it. This is also super nice because there
is zero run-time overhead vs. having written the "self." myself, everything
happens compile time.
Now for the question. I'd like to also to be able automatically pass the
"self" argument to functions, so that I could write something like,
@self function inc2()
inc()
inc()
end
and it would produce
function inc2(self::mytype)
inc(self)
inc(self)
end
For this though, my macro needs to somehow figure out that "inc" was also
defined with @self (since it shouldn't blindly add self as a first arg so
other non-@self'ed function calls). Is this possible in Julia? I suppose
somehow the macro must access the global scope where the expression is
being evaluated? I'm not entirely sure that's doable. I'm happy to take any
tips how to achieve this though, especially ones incurring minimal overhead
for the rewritten function. Thanks!