I think I’ve finally got it, thanks to everyone in this thread for the help!!!
Here’s the code after it evolved (hehe) through this thread:
mutable struct state
x::Int64
end
generate_candidate_func(hook) = eval(
quote
state -> begin
for t in 1:1000
$hook
end
end
end
)
function eval_candidate_func(hook::Expr, invoke_times=1)
candidate_func = generate_candidate_func(hook)
sx_max = 0
for i in 1:invoke_times
s = state(0)
Base.invokelatest(candidate_func, s) # mutates s
sx_max = max(s.x, sx_max)
end
return sx_max
end
function search_candidate_func(n_iter)
desired_score = 50
best_score = Inf
for i in 1:n_iter
hook = :(state.x = $i)
sx = eval_candidate_func(hook)
score = min(best_score, abs(desired_score - s.x))
end
best_score
end
The compilation takes short time, and the execution of the compiled function takes negligible time:
myhook = :(state.x = 50)
eval_candidate_func(myhook, 1) # warm up hook before benchmark
@btime eval_candidate_func($myhook, 1)
# 3.202 ms (17874 allocations: 1.08 MiB)
@btime eval_candidate_func($myhook, 100)
# 3.220 ms (17973 allocations: 1.08 MiB)
@btime eval_candidate_func($myhook, 1000)
# 3.387 ms (18873 allocations: 1.09 MiB)
So to answer directly to my simple example from the original question
A working solution for my wrongly phrased question would be:
function ex_v(v_init::Integer, hook::Expr)
eval(quote
v = $v_init
$hook
return v
end)
end
@assert ex_v(0, :(v = v+1)) == 1
But the solution which I actually needed would be:
function ex_v_func(hook::Expr)
eval(
quote
v_init::Integer -> begin
v = v_init
$hook
return v
end
end
)
end
ex_v = ex_v_func(:(v = v+1))
@assert ex_v(0)==1