On Saturday, February 13, 2016 at 8:58:46 AM UTC-5, Robert Feldt wrote:
>
> Good clarifications and example, thanks.
>
> To answer your question I will not write test code/blocks that updates
> variables outside the block but someone else might and currently Base.Test
> (in 0.5-dev) would allow it. I want my PR to retain current functionality
> as much as possible. But you are right, tests in general shouldn't have
> effect outside the test...
>
That's reasonable.
Do you see any risks with doing it the run_code way in my foo2 macro?
>
I'm not sure about the `eval`, but the for loop introduces a scope block
(soft scope?)
for x=1:10
b = 80
end
@show b # UndefVarError
It'll only work if b is declared/initialized above the loop. For a test,
that seems like a problem, for instance, this won't work
@test begin
env = Environment()
foo1(env)
end
@test begin
foo2(env
end
I don't think that's solvable. There are some built-in macros that suffer
from the same thing, eg.
@profile oo=10
oo # UndefVarError
Personally, I would use `do` instead of `begin`:
@test() do
...
end
That way it's clear that we're introducing a new function scope. But I can
see why people wouldn't want to change the current syntax.
Cédric
>
> Den lördag 13 februari 2016 kl. 14:49:41 UTC+1 skrev Cedric St-Jean:
>>
>> I'm not sure there's a way to achieve exactly what you're asking, but
>> I'll note that your problem comes from Julia's scoping rules for
>> assignment. I.e. if you replace
>>
>> a = 1
>> @foo1 begin
>> a += 1 # UndefVarError!
>> end
>>
>> with
>>
>> a = 1
>> @foo1 begin
>> 2 * a
>> end
>>
>> then you don't get any UndefVarError. Why do you want assignment inside a
>> test to apply outside the test? Not sure if this will apply to you, but I
>> sometimes have code that's written this way to get around the scoping rules:
>>
>> w = initial_value()
>> w = fixed_point(max_iter=10) do w
>> w = blah(w)
>> ...
>> w
>> end
>>
>>
>> On Saturday, February 13, 2016 at 7:41:35 AM UTC-5, Robert Feldt wrote:
>>>
>>> To clarify the possible solution hinted at in my previous message here
>>> is code to show how it could be done:
>>>
>>> function run_code(t::Type{Runner}, runnername::Symbol, block::Expr)
>>> quote
>>> for i in 1:($runnername.reps)
>>> $block
>>> end
>>> end
>>> end
>>>
>>> macro foo2(runnertypename, ex)
>>> rt = eval(runnertypename) # But what if Runner type not defined in
>>> global scope? Risky!?
>>> quote
>>> local r = $(esc(runnertypename))()
>>> $(run_code(rt, :r, esc(ex)))
>>> end
>>> end
>>> a = 1
>>> @foo2 Runner begin
>>> a += 1
>>> end
>>> @show(a);
>>>
>>> This works as intended and gives a lot of power for people to write
>>> their own Runners but feels a bit inelegant and error prone. If there is a
>>> more idiomatic way please share.
>>>
>>> /Robert
>>>
>>> Den lördag 13 februari 2016 kl. 12:16:43 UTC+1 skrev Robert Feldt:
>>>>
>>>> When working on a possible PR for the new Base.Test infrastructure I
>>>> run into a design problem of how to allow flexible control of how the expr
>>>> available to a macro is executed. Sorry if this is trivial but I'd
>>>> appreciate any design ideas/pointers. Simplified code below to try and
>>>> explain what I mean.
>>>>
>>>> # A macro can eval in the calling context:
>>>> macro foo(ex)
>>>> quote
>>>> $(esc(ex))
>>>> end
>>>> end
>>>> a = 1
>>>> @foo begin
>>>> a += 1
>>>> end
>>>> @show(a); # a = 2
>>>>
>>>> # But lets say I want to be able to dynamically control how the expr
>>>> # of a macro is executed via a Runner (this is a convoluted/dummy
>>>> example but
>>>> # simplified from a more complex case where there are multiple types
>>>> # of Runners)
>>>> type Runner
>>>> reps::Int64
>>>> Runner() = new(2)
>>>> end
>>>> run(f::Function, r::Runner) = begin
>>>> for i in 1:r.reps
>>>> f()
>>>> end
>>>> end
>>>>
>>>> # Is there any way I can "package" the expr a macro has so that its
>>>> execution
>>>> # is controlled by an instance of the Runner above while still
>>>> evaluating
>>>> # the expr in the scope where the macro is called?
>>>>
>>>> # First try:
>>>> macro foo1(ex)
>>>> quote
>>>> r = Runner()
>>>> run(r) do # UndefVarError since do-block creates new scope?
>>>> $(esc(ex))
>>>> end
>>>> end
>>>> end
>>>> @foo1 begin
>>>> a += 1 # UndefVarError!
>>>> end
>>>> @show(a);
>>>>
>>>> # What I could do is maybe have dispatch on Type{Runner}to generate
>>>> the run code to be spliced into
>>>> # the macro but this seems inelegant and maybe error prone since it
>>>> mixes compile-time
>>>> # and runtime (since Type{Runner} is needed at macro expansion time).
>>>> # Any ideas on a clean and elegant way to accomplish this?
>>>>
>>>> # Further details at the bottom of:
>>>> https://github.com/JuliaLang/julia/pull/14971
>>>>
>>>> Thanks!
>>>>
>>>