Neat macro.
 

> 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?
>

You could have a global Set that would contain the names of the functions 
that were defined with @self. But IMO this is going to bite you at one 
point or another.

FYI Mauro's package has something similar 
<http://parametersjl.readthedocs.io/en/latest/manual/>.

I would suggest using a global variable, if you want to avoid explicitly 
passing `self` all over the place. It would look like this:

const self = Array{mytype}()   # trick to avoid the globals' poor 
performance

@self function foo()
   x = x + 1   # expands into self[].x = self[].x + 1
end

@with_self(mytype(200)) do
   # expands into 
   # try
   #    ... save the current value of self
   #    global self[] = mytype(200)
   #    ... code
   # finally
   #    global self[] = ...restore previous value
   # end
   ...
end

I used this idiom in Common Lisp all the time. It's strictly equivalent to 
passing the object around to every function, and doesn't break the 
"functionalness" of the code.

Cédric

On Thursday, July 21, 2016 at 4:01:20 PM UTC-4, Marius Millea wrote:
>
> 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!
>
>

Reply via email to