Re: [julia-users] Re: accessing an expression's global scope from macro

2016-07-26 Thread Fábio Cardeal
Alright! Here's a more complete and correct demonstration (And one I can 
fix without sending e-mails to everyone):
  https://gist.github.com/fcard/1bf78e9d4f6ea3be76518f6a0fbe0283

Sorry! Just don't wanna end the thread with wrong information, last one I 
promise!
(I will make sure to have any significant information in some editable 
place and link there from now on)
(eeep)

Also, Marius, can you confirm that the global was in fact the problem and 
it works fine now?
No hurry, just asking since I am here + things got a bit off-topic :P


Re: [julia-users] Re: accessing an expression's global scope from macro

2016-07-24 Thread Fábio Cardeal

That explanation is a bit off actually, it's not that f1 can't optimize for 
t, it's that f1 has to do method lookup every time it's called.


  type X; x::Int end
  g1(x) = x.x+1
  g2(x::X) = x.x+1

  x = X(1)
  const y = X(1)

  @code_warntype g1(x)
  #...
  #  begin 
  #return 
(Base.box)(Int64,(Base.add_int)((Core.getfield)(x::X,:x)::Int64,1))
  #  end::Int64

  @code_warntype g2(x)
  #...
  #  begin
  #return 
(Base.box)(Int64,(Base.add_int)((Core.getfield)(x::X,:x)::Int64,1))
  #  end::Int64

  # same generated code, but...

  h1() = g1(x)
  h2() = g2(x)

  @code_warntype h1()
  #  ...
  #  begin 
  #SSAValue(0) = Main.x
  #return ((Core.getfield)(SSAValue(0),:x)::Any + 1)::Any
  #  end::Any

  @code_warntype h2()
  #  ...
  #  begin 
  #return (Main.g2)(Main.x)::Int64
  #  end::Int64

  # The specialized method can't be found beforehand on h1!

  @timeit h1()
  #  100 loops, best of 3: 228.03 ns per loop
  #  2.28028349e-7

  @timeit h2()
  #  100 loops, best of 3: 185.79 ns per loop
  #  1.85790018e-7


You probably already know this, but correcting my wrong info and explaining 
it makes me feel better :P



Re: [julia-users] Re: accessing an expression's global scope from macro

2016-07-24 Thread Fábio Cardeal
Actually the performance drop there is just because of the always looming 
threat of global var inefficience :P

Because the var t is global, f1 can't optimize for its type. But f2 has a 
type declared on its argument, so it can. Remove the ::mytype on f2 or make 
t a const and you'll see no significant performance difference.

I went ahead and added the type deckle to @selftype anyway, cuz' why not.

Em domingo, 24 de julho de 2016 11:58:27 UTC-3, Marius Millea escreveu:
>
> I think you are right btw, the compiler got rid of the wrapper function 
> for the "+" call, since all I see above is Base.add_float.
>
> On Sun, Jul 24, 2016 at 4:55 PM, Marius Millea  > wrote:
>
>> Here's my very simple test case. I will also try on my actual code. 
>>
>> using SelfFunctions
>> using TimeIt
>>
>> @selftype self type mytype
>> x::Float64
>> end
>>
>> t = mytype(0)
>>
>>
>> # Test @self'ed version:
>>
>> @self @inline function f1()
>> 1+x
>> end
>>
>> @timeit f1(t)
>> println(@code_warntype(f1(t)))
>>
>> # 100 loops, best of 3: 100.64 ns per loop
>> # Variables:
>> #   sf::SelfFunctions.SelfFunction{###f1_selfimpl#271}
>> #   args::Tuple{mytype}
>> # 
>> # Body:
>> #   begin 
>> #   # meta: location /home/marius/workspace/selffunctions/test.jl 
>> ##f1_selfimpl#271 11
>> #   SSAValue(1) = 
>> (Core.getfield)((Core.getfield)(args::Tuple{mytype},1)::mytype,:x)::Float64
>> #   # meta: pop location
>> #   return 
>> (Base.box)(Base.Float64,(Base.add_float)((Base.box)(Float64,(Base.sitofp)(Float64,1)),SSAValue(1)))
>> #   end::Float64
>>
>>
>>
>>
>> # Test non-@self'ed version:
>>
>> @inline function f2(t::mytype)
>> 1+t.x
>> end
>>
>> @timeit f2(t)
>> println(@code_warntype(f2(t)))
>> 
>> # 1000 loops, best of 3: 80.13 ns per loop
>> # Variables:
>> #   #self#::#f2
>> #   t::mytype
>> # 
>> # Body:
>> #   begin 
>> #   return 
>> (Base.box)(Base.Float64,(Base.add_float)((Base.box)(Float64,(Base.sitofp)(Float64,1)),(Core.getfield)(t::mytype,:x)::Float64))
>> #   end::Float64
>> # nothing
>>
>>
>> I'm not sure if its the creation of the SSAValue intermediate value or 
>> the extra getfield lookup, but you can see it slows down from ~80 to 
>> ~100ns. 
>>
>>
>> Marius
>>
>>
>>
>> On Sunday, July 24, 2016 at 3:52:38 PM UTC+2, Fábio Cardeal wrote:
>>>
>>> The compiler is pretty smart about removing these extra function calls, 
>>> so I didn't get any extra overhead on my test cases. I went ahead and added 
>>> `@inline` to the selfcall deckles. You can also do this:
>>>
>>>   @self @inline function inc2()
>>> inc()
>>> inc()
>>>   end
>>>  
>>> Update from the gist and try using some @inlines and see if it helps. 
>>> You can also send me your test cases if you want.
>>>
>>> In general, these techniques of adding and using compile time 
>>> information shouldn't cause any definite slowdown, even if we need to do 
>>> some tweaking with them meta tags. The compiler isn't perfect about this 
>>> yet, but I think our case is covered. (I hope?)
>>>
>>
>

Re: [julia-users] Re: accessing an expression's global scope from macro

2016-07-24 Thread Marius Millea
I think you are right btw, the compiler got rid of the wrapper function for
the "+" call, since all I see above is Base.add_float.

On Sun, Jul 24, 2016 at 4:55 PM, Marius Millea 
wrote:

> Here's my very simple test case. I will also try on my actual code.
>
> using SelfFunctions
> using TimeIt
>
> @selftype self type mytype
> x::Float64
> end
>
> t = mytype(0)
>
>
> # Test @self'ed version:
>
> @self @inline function f1()
> 1+x
> end
>
> @timeit f1(t)
> println(@code_warntype(f1(t)))
>
> # 100 loops, best of 3: 100.64 ns per loop
> # Variables:
> #   sf::SelfFunctions.SelfFunction{###f1_selfimpl#271}
> #   args::Tuple{mytype}
> #
> # Body:
> #   begin
> #   # meta: location /home/marius/workspace/selffunctions/test.jl
> ##f1_selfimpl#271 11
> #   SSAValue(1) =
> (Core.getfield)((Core.getfield)(args::Tuple{mytype},1)::mytype,:x)::Float64
> #   # meta: pop location
> #   return
> (Base.box)(Base.Float64,(Base.add_float)((Base.box)(Float64,(Base.sitofp)(Float64,1)),SSAValue(1)))
> #   end::Float64
>
>
>
>
> # Test non-@self'ed version:
>
> @inline function f2(t::mytype)
> 1+t.x
> end
>
> @timeit f2(t)
> println(@code_warntype(f2(t)))
>
> # 1000 loops, best of 3: 80.13 ns per loop
> # Variables:
> #   #self#::#f2
> #   t::mytype
> #
> # Body:
> #   begin
> #   return
> (Base.box)(Base.Float64,(Base.add_float)((Base.box)(Float64,(Base.sitofp)(Float64,1)),(Core.getfield)(t::mytype,:x)::Float64))
> #   end::Float64
> # nothing
>
>
> I'm not sure if its the creation of the SSAValue intermediate value or the
> extra getfield lookup, but you can see it slows down from ~80 to ~100ns.
>
>
> Marius
>
>
>
> On Sunday, July 24, 2016 at 3:52:38 PM UTC+2, Fábio Cardeal wrote:
>>
>> The compiler is pretty smart about removing these extra function calls,
>> so I didn't get any extra overhead on my test cases. I went ahead and added
>> `@inline` to the selfcall deckles. You can also do this:
>>
>>   @self @inline function inc2()
>> inc()
>> inc()
>>   end
>>
>> Update from the gist and try using some @inlines and see if it helps. You
>> can also send me your test cases if you want.
>>
>> In general, these techniques of adding and using compile time information
>> shouldn't cause any definite slowdown, even if we need to do some tweaking
>> with them meta tags. The compiler isn't perfect about this yet, but I think
>> our case is covered. (I hope?)
>>
>


Re: [julia-users] Re: accessing an expression's global scope from macro

2016-07-24 Thread Marius Millea
Here's my very simple test case. I will also try on my actual code. 

using SelfFunctions
using TimeIt

@selftype self type mytype
x::Float64
end

t = mytype(0)


# Test @self'ed version:

@self @inline function f1()
1+x
end

@timeit f1(t)
println(@code_warntype(f1(t)))

# 100 loops, best of 3: 100.64 ns per loop
# Variables:
#   sf::SelfFunctions.SelfFunction{###f1_selfimpl#271}
#   args::Tuple{mytype}
# 
# Body:
#   begin 
#   # meta: location /home/marius/workspace/selffunctions/test.jl 
##f1_selfimpl#271 11
#   SSAValue(1) = 
(Core.getfield)((Core.getfield)(args::Tuple{mytype},1)::mytype,:x)::Float64
#   # meta: pop location
#   return 
(Base.box)(Base.Float64,(Base.add_float)((Base.box)(Float64,(Base.sitofp)(Float64,1)),SSAValue(1)))
#   end::Float64




# Test non-@self'ed version:

@inline function f2(t::mytype)
1+t.x
end

@timeit f2(t)
println(@code_warntype(f2(t)))

# 1000 loops, best of 3: 80.13 ns per loop
# Variables:
#   #self#::#f2
#   t::mytype
# 
# Body:
#   begin 
#   return 
(Base.box)(Base.Float64,(Base.add_float)((Base.box)(Float64,(Base.sitofp)(Float64,1)),(Core.getfield)(t::mytype,:x)::Float64))
#   end::Float64
# nothing


I'm not sure if its the creation of the SSAValue intermediate value or the 
extra getfield lookup, but you can see it slows down from ~80 to ~100ns. 


Marius



On Sunday, July 24, 2016 at 3:52:38 PM UTC+2, Fábio Cardeal wrote:
>
> The compiler is pretty smart about removing these extra function calls, so 
> I didn't get any extra overhead on my test cases. I went ahead and added 
> `@inline` to the selfcall deckles. You can also do this:
>
>   @self @inline function inc2()
> inc()
> inc()
>   end
>  
> Update from the gist and try using some @inlines and see if it helps. You 
> can also send me your test cases if you want.
>
> In general, these techniques of adding and using compile time information 
> shouldn't cause any definite slowdown, even if we need to do some tweaking 
> with them meta tags. The compiler isn't perfect about this yet, but I think 
> our case is covered. (I hope?)
>


Re: [julia-users] Re: accessing an expression's global scope from macro

2016-07-24 Thread Fábio Cardeal
The compiler is pretty smart about removing these extra function calls, so 
I didn't get any extra overhead on my test cases. I went ahead and added 
`@inline` to the selfcall deckles. You can also do this:

  @self @inline function inc2()
inc()
inc()
  end
 
Update from the gist and try using some @inlines and see if it helps. You 
can also send me your test cases if you want.

In general, these techniques of adding and using compile time information 
shouldn't cause any definite slowdown, even if we need to do some tweaking 
with them meta tags. The compiler isn't perfect about this yet, but I think 
our case is covered. (I hope?)


Re: [julia-users] Re: accessing an expression's global scope from macro

2016-07-24 Thread Marius Millea
Very nice! Didn't understand your hint earlier but now I do!

My only problem with this solution is the (perhaps unavoidable) run-time
overhead, since every single function call gets wrapped in one extra
function call. With a very simple test function that just does some
arithmetic, I'm seeing about a 25% slow down using this. I wonder if
there's other ways to achieve functionally exactly what you've done here
but that involve something faster than a function call? In any case, this
is nice and I may use it anyway.

On Sun, Jul 24, 2016 at 3:21 AM, Fábio Cardeal  wrote:

> Hyy :)
>
> I made an implementation:
> https://gist.github.com/fcard/f356b01d5bb160dd486b9518ac292582
>
> Julia 0.5 only. Enjoy...? Bye bye
>
> --
> -
>


[julia-users] Re: accessing an expression's global scope from macro

2016-07-23 Thread Fábio Cardeal
Hyy :)

I made an implementation: 
https://gist.github.com/fcard/f356b01d5bb160dd486b9518ac292582

Julia 0.5 only. Enjoy...? Bye bye

--
-


[julia-users] Re: accessing an expression's global scope from macro

2016-07-22 Thread Fábio Cardeal
Clearly the ingredient missing here is some evil type magic... Hint:


  immutable SelfFunction
f::Function
  end

  selfcall(f::SelfFunction, t::MyType, args...) = f(t, args...)
  selfcall(f, t::MyType, args...) = f(args...)

  const inc2 = SelfFunction((t::MyType) -> begin
selfcall(inc, t)
selfcall(inc, t)
  end)


Re: [julia-users] Re: accessing an expression's global scope from macro

2016-07-22 Thread Mauro
On Fri, 2016-07-22 at 10:18, Marius Millea  wrote:
> Yea, thats a good point. Granted for my purpose I'm never going to be
> redefining these types mid program. I suppose one might extend your @unpack
> to work on an expression and do the substitution recursively like my thing
> does, then you could write,
>
> @unpack aa: a function(x,aa:A)
>   sin(2pi/a*x)
>   a = 3 #can also assign without repacking
> end

For functions and loops, it seems much better to have the `@unpack`
inside, no?  For begin-end blocks, you could just use a let instead:

  let
@unpack aa: a
sin(2pi/a*x)
  end

(Or are you worried about performance?  The extra bindings introduced
are optimized away (I think).)

> which seems slightly less hacky than what I'm doing but serves a similar
> purpose.
>
> Marius
>
>
> On Fri, Jul 22, 2016 at 9:01 AM, Mauro  wrote:
>
>>
>> On Fri, 2016-07-22 at 01:02, Marius Millea  wrote:
>> >> FYI Mauro's package has something similar
>> >> .
>> >>
>> >
>> > Some interesting stuff in there, thanks!
>>
>> The problem with your `@self` and with Parameters.jl's
>> `@unpack_SomeType` macros is that it is easy to introduce bugs.
>> Consider:
>>
>>   type A # and register it with @self
>>  a
>>   end
>>   @self f(x,aa:A) = sin(2pi/a*x)
>>
>> Sometime later you refactor type A:
>>
>>   type A
>>  a
>>  pi
>>   end
>>
>> now your function f is broken.  So, for every change in type A you need
>> to check all functions which use `@self`.
>>
>> Instead I now use the @unpack macro (and its companion @pack), also part
>> of Paramters.jl.  Then above f becomes
>>
>>   function f(x,aa::A)
>> @unpack aa: a
>> sin(2pi/a*x)
>>   end
>>
>> This is still much more compact than writing out all the aa.a, etc. (if
>> there are lots of field accesses) but safe.  Also, it clearly states, at
>> the top of the function, which fields of a type are actually used.
>>


Re: [julia-users] Re: accessing an expression's global scope from macro

2016-07-22 Thread Marius Millea
Yea, thats a good point. Granted for my purpose I'm never going to be
redefining these types mid program. I suppose one might extend your @unpack
to work on an expression and do the substitution recursively like my thing
does, then you could write,

@unpack aa: a function(x,aa:A)
  sin(2pi/a*x)
  a = 3 #can also assign without repacking
end

which seems slightly less hacky than what I'm doing but serves a similar
purpose.

Marius


On Fri, Jul 22, 2016 at 9:01 AM, Mauro  wrote:

>
> On Fri, 2016-07-22 at 01:02, Marius Millea  wrote:
> >> FYI Mauro's package has something similar
> >> .
> >>
> >
> > Some interesting stuff in there, thanks!
>
> The problem with your `@self` and with Parameters.jl's
> `@unpack_SomeType` macros is that it is easy to introduce bugs.
> Consider:
>
>   type A # and register it with @self
>  a
>   end
>   @self f(x,aa:A) = sin(2pi/a*x)
>
> Sometime later you refactor type A:
>
>   type A
>  a
>  pi
>   end
>
> now your function f is broken.  So, for every change in type A you need
> to check all functions which use `@self`.
>
> Instead I now use the @unpack macro (and its companion @pack), also part
> of Paramters.jl.  Then above f becomes
>
>   function f(x,aa::A)
> @unpack aa: a
> sin(2pi/a*x)
>   end
>
> This is still much more compact than writing out all the aa.a, etc. (if
> there are lots of field accesses) but safe.  Also, it clearly states, at
> the top of the function, which fields of a type are actually used.
>


Re: [julia-users] Re: accessing an expression's global scope from macro

2016-07-22 Thread Mauro

On Fri, 2016-07-22 at 01:02, Marius Millea  wrote:
>> FYI Mauro's package has something similar
>> .
>>
>
> Some interesting stuff in there, thanks!

The problem with your `@self` and with Parameters.jl's
`@unpack_SomeType` macros is that it is easy to introduce bugs.
Consider:

  type A # and register it with @self
 a
  end
  @self f(x,aa:A) = sin(2pi/a*x)

Sometime later you refactor type A:

  type A
 a
 pi
  end

now your function f is broken.  So, for every change in type A you need
to check all functions which use `@self`.

Instead I now use the @unpack macro (and its companion @pack), also part
of Paramters.jl.  Then above f becomes

  function f(x,aa::A)
@unpack aa: a
sin(2pi/a*x)
  end

This is still much more compact than writing out all the aa.a, etc. (if
there are lots of field accesses) but safe.  Also, it clearly states, at
the top of the function, which fields of a type are actually used.


Re: [julia-users] Re: accessing an expression's global scope from macro

2016-07-21 Thread Yichao Yu
On Thu, Jul 21, 2016 at 7:02 PM, Marius Millea  wrote:
>
>
> On Thu, Jul 21, 2016 at 11:37 PM, Cedric St-Jean 
> wrote:
>>
>> 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.
>
>
> Yea certainly a possibility, although even this doesn't seem that robust
> since you're only doing this based on function name, and you don't know if
> its referring to a different function in any given call environment. I'm
> starting to doubt its truly possible at compile time, although still
> thinking

No it's not.

>
>
>
>>
>>
>> FYI Mauro's package has something similar.
>
>
> Some interesting stuff in there, thanks!
>
>>
>>
>> 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!
>>>
>


Re: [julia-users] Re: accessing an expression's global scope from macro

2016-07-21 Thread Marius Millea
On Thu, Jul 21, 2016 at 11:37 PM, Cedric St-Jean 
wrote:

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

Yea certainly a possibility, although even this doesn't seem that robust
since you're only doing this based on function name, and you don't know if
its referring to a different function in any given call environment. I'm
starting to doubt its truly possible at compile time, although still
thinking




>
> FYI Mauro's package has something similar
> .
>

Some interesting stuff in there, thanks!


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


[julia-users] Re: accessing an expression's global scope from macro

2016-07-21 Thread Cedric St-Jean
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 
.

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