this gets a little tricky (and subject to change) because there's lots of
uses for env. Specifically, you need to be looking at the env for a
particular Method, not the env for a generic Function.
julia> function f()
x = 1
g() = x+=1
g
end
f (generic function with 1 method)
julia> code_typed(f,())
1-element Array{Any,1}:
:($(Expr(:lambda, {}, {{:x,:g},{{:x,Any,7},{:g,Function,2}},{}}, :(begin
# none, line 2:
NewvarNode(:x)
x = 1 # line 3:
$(Expr(:method, :g, :((top(tuple))((top(tuple))(),(top(tuple))())),
AST(:($(Expr(:lambda, {},
{{symbol("#s3")},{{symbol("#s3"),Any,18}},{{:x,Any,7}}}, :(begin # none,
line 3:
#s3 = x + 1
x = #s3
return #s3
end))))), false)) # line 4:
return g::F
end::F))))
julia> code_typed(f(),())
1-element Array{Any,1}:
:($(Expr(:lambda, {},
{{symbol("#s3")},{{symbol("#s3"),Any,18}},{{:x,Any,7}}}, :(begin # none,
line 3:
#s3 = x + 1
x = #s3
return #s3
end))))
julia> methods(f(),())[1].func.env
(Box(1),)
Observe too that 2nd entry in the Expr(:lambda).args. The first argument is
the list of symbols that the function takes as arguments. The second
argument is some metadata on the variables in the function. The first entry
is the list of local variables. The second entry is metadata and inferred
type for those variables (18 means SSA in the above example). The third
entry is the one we are interested in: it gives the ordered list of
variables in env. We can find out what the "7" means by at the comments in
the source code that sets them:
https://github.com/JuliaLang/julia/blob/8c02093174a1c28d61f57c023f6cf592c01174e3/src/julia-syntax.scm#L2817
Sorry Jeff, I'm giving away your secrets. Now you'll have to go change it
(as part of the planned changes to make closures more efficient / faster) :P
On Thu, Oct 16, 2014 at 12:27 AM, Nikolas Tezak <[email protected]>
wrote:
> Jameson, that sounds intriguing! Could you maybe flesh that out in more
> detail? I checked out the .env attribute of a Function but I couldn't find
> any reference to the captured variables. Is there any more sophisticated
> way to check out the contents of .env than calling
> names(my_function.env)
> ?
>
> Thanks!
> Nik
>
> On Wednesday, October 15, 2014 8:47:51 PM UTC-7, Jameson wrote:
>>
>> you can inspect the `.env` field of a method to get a reference to the
>> closure variables
>>
>> On Wed, Oct 15, 2014 at 6:26 PM, Nikolas Tezak <[email protected]>
>> wrote:
>>
>>> Hi Isaiah, thanks for your reply! Yeah, I figured my use case is
>>> probably a little exotic. I have thought very hard about using macros, but
>>> either I haven't quite understood them yet (likely!) or they aren't the
>>> best solution in this instance.
>>> Maybe some sort of symbolic approach could work where I only store
>>> Expression objects for each circuit element that get concatenated and
>>> compiled when a whole circuit is simulated.
>>>
>>> Anyway, thanks!
>>> Nik
>>>
>>> On Wednesday, October 15, 2014 11:11:42 AM UTC-7, Isaiah wrote:
>>>>
>>>> Given the constructed sys1_ode method is there someway to dynamically
>>>>> access the captured variables, i.e. those that come from its closure?
>>>>
>>>>
>>>> I'm not sure I entirely follow, but: you can use the variables p1 and
>>>> p2 inside the inner function and it will Just Work (scoping rules). But it
>>>> is not possible to interrogate the closure environment from inside the
>>>> enclosed function.
>>>>
>>>> Also, have you considered macros? They won't help to interrogate the
>>>> closure environment, but are a much better option for AST rewriting than
>>>> using code_lowered.
>>>>
>>>> On Tue, Oct 14, 2014 at 2:14 PM, Nikolas Tezak <[email protected]>
>>>> wrote:
>>>>
>>>>> Hi all,
>>>>>
>>>>> in my research I run numerical simulations (ODEs and SDEs) for circuit
>>>>> models that can be composed, i.e.,
>>>>> each system has an ode that modifies in-place the elements of an
>>>>> output array based on the current state variable.
>>>>> Moreover, dimensionality of each system may vary.
>>>>>
>>>>> function sys1_ode(t, x, xdot)
>>>>> xdot[1] = # some function of x, t
>>>>> xdot[2] = # some other function of x, t
>>>>> end
>>>>>
>>>>>
>>>>> function sys2_ode(t, x, xdot)
>>>>> xdot[1] = # some expression with x, t
>>>>> xdot[2] = # some other expression with x, t
>>>>> xdot[3] = # some other expression with x, t
>>>>> end
>>>>>
>>>>>
>>>>> What I would like to do is use metaprogramming to construct a combined
>>>>> ode for both systems where the state vectors are just stacked. For each
>>>>> system I compute the offset within the state vector (0 for sys1 and 2 for
>>>>> sys2) and then modify and recombine the function code as follows
>>>>>
>>>>> function sys12_ode(t, x, xdot)
>>>>> xdot[1+0] = # some expression with x[1:2], t
>>>>> xdot[2+0] = # some other expression with x[1:2], t
>>>>> xdot[1+2] = # some expression with x[1+2:3+2], t
>>>>>
>>>>> xdot[2+2] = # some expression with x[1+2:3+2], t
>>>>> xdot[3+2] = # some expression with x[1+2:3+2], t
>>>>>
>>>>> end
>>>>>
>>>>>
>>>>> So far, that would seem to be quite straightforward and I think I
>>>>> could get this working by calling code_lowered(sys1_ode) and using the
>>>>> rewritten output to construct an AST for the combined function.
>>>>>
>>>>> The difficulty now arises when my sys1 and sys2 odes are defined with
>>>>> some internal parameters that are not passed as an argument but rather
>>>>> through a closure from the surrounding scope, i.e. I have some ode
>>>>> factory:
>>>>>
>>>>> funcion make_sys1_ode(p1, p2)
>>>>> function sys1_ode(t, x, xdot)
>>>>> xdot[1] = # some expression with x, t AND p1, p2
>>>>> xdot[2] = # some expression with of x, t AND p1, p2
>>>>> end
>>>>> sys1_ode
>>>>> end
>>>>>
>>>>> Given the constructed sys1_ode method is there someway to dynamically
>>>>> access the captured variables, i.e. those that come from its closure?
>>>>> Otherwise, I suppose I could resort to passing parameters via an extra
>>>>> ODE argument, but it would be super nice if I could avoid this.
>>>>> The reason why I am trying to implement things this way is to speed up
>>>>> ODE evaluation for very large complex (i.e. nested) circuits by expanding
>>>>> out all ODE bodies into a single function.
>>>>>
>>>>> I hope this write-up makes sense.
>>>>> Thanks,
>>>>> Nik
>>>>>
>>>>>
>>>>
>>