I want to use @eval to generate code that does the same thing for different symbols. I keep going back to this example from Logging.jl, even though it’s not the best example. Here is my contrived use case:
"""
find all elements of `x` and `y` according to some rule and put them into aptly named dictionaries. return the dictionaries.
"""
function func()
x = rand(10)
y = rand(10)
x_idx = x.< 0.5
y_idx = y.> 0.5
for (gg,idx,vals) in ((:xDict,:x_idx,:x),(:yDict,:y_idx,:y))
@eval begin
($gg) = Dict(zip($idx,$vals[$idx]))
end
end
return (xDict,yDict)
end
julia> func()
ERROR: UndefVarError: x_idx not defined
in eval(::Module, ::Any) at ./boot.jl:234
in eval(::Module, ::Any) at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
in func() at ./REPL[84]:9
function func()
x = rand(10)
y = rand(10)
x_idx = find(x.< 0.5)
y_idx = find(y.> 0.5)
for (gg,idx,vals) in ((:xDict,x_idx,x),(:yDict,y_idx,y))
@eval begin
($gg) = Dict(zip($idx,$vals[$idx]))
end
end
return (xDict,yDict)
end
yes! cool, thanks. bit stupid on my behalf. i think i got it now, actually much simpler than I thought. this is literally just interpolating symbols into expressions…
Note that is “solution” is not interpolating symbols into the expression. Also this is not the proper way to do metaprogramming since almost everything are running in the global scope (xDict and yDict are global variables). It is impossible to use eval to get or set local variables and it is extremely unlikely it will ever be possible.
A eval statement will always evaluate in the global scope of the module it is located in. Each module has its own separate glocal scope. Your second xDict sits in the global scope of Main whereas the one from the eval is in Testm:
julia> module Testm
function func()
x = rand(10)
y = rand(10)
x_idx = find(x.< 0.5)
y_idx = find(y.> 0.5)
for (gg,idx,vals) in ((:xDict,x_idx,x),(:yDict,y_idx,y))
@eval begin
($gg) = Dict(zip($idx,$vals[$idx]))
end
end
return (xDict,yDict)
end
end
Testm
julia> Testm.xDict
ERROR: UndefVarError: xDict not defined
julia> Testm.func() # this creates Testm.xDict
(Dict(7=>0.366333,4=>0.193313,3=>0.341151,5=>0.141716,8=>0.479359),Dict(3=>0.865234,5=>0.722114,8=>0.759312))
julia> Testm.xDict
Dict{Int64,Float64} with 5 entries:
7 => 0.366333
4 => 0.193313
3 => 0.341151
5 => 0.141716
8 => 0.479359
This is an oversimplification. One should not use eval unless one intends to have effects at global scope. But if one is building new types and methods specialized on those types, functions may be helpful. Graham (“On Lisp”) says to use functions rather than macros where possible. I suspect that some of the macro code in the spectacular StaticArrays package could be cleaned up or made more flexible by use of functions employing eval. Perhaps @andyferris has tried this and found otherwise?
Sure, but not by much. There are few cases that you need to do it but I don’t think any cases mentioned anywhere in this thread so far is one of those.
In most of those cases you shouldn’t put the code in a function to start with.
At a quick glance that’s exactly the opposite. This is also why I don’t want to use the few corner cases to confuse people that don’t need to worry about it. For most people, remember to not use eval in a function should be good enough.
I’m not certain how to use eval nicely in StaticArrays. The macros (e.g. v = @SVector [1,2,3]) are just there to convert common Julia array creation syntax into the appropriate StaticArrays constructor, because (a) the types have many type parameters and can be pain to write by hand and (b) people are familar with the standard array creation syntax. I could (and should) use functions inside my (disgusting) macros to amalgamate common code. Calling eval however would be very slow.
(as an aside, where I would love to use is in a @generated function’s generator but that is forbidden because the compiler writers (quite reasonably) don’t want to e.g. guarantee that function generators run exactly once, or deal with complications that mutating the state of the system in the middle of certain compilation steps would involve, which is fair enough! )
But yes, in (rare) cases where you are doing a lot of complicated code generation or need to something special in __init__() then it might be reasonable to use eval() in functions that usually are called once (or a fixed number of times) when the package compiles or is loaded.