This is an interesting idea and probably Casette.jl or IRTools.jl is the best way to solve it.
On the other hand, to some extent it can be solved with usual macros (sorry, I am just thinking aloud, not sure if its doable in reality).
Idea is that your code roughly equivalent to
struct Foo
world # pointer to something interesting and contextual
value # just a Float64 perhaps
end
let world = "hello"
mkFoo(x) = Foo(world, x)
println(mkFoo(1))
end
i.e. if we create closure inside let
block, than we are good to go. Of course, your example use function declaration outside of let
block, in which case you can’t declare closure.
What can be done, is “storing” closure declaration somewhere and applying it afterwards, so for the end user, it looks like
@with_context world mkFoo(x) = Foo(world, x)
@in_context world = World(...) begin
x = mkFoo(3)
y = mkFoo(5)
...
end
And macro @in_context
should generate these lines
let world = World(...) begin
mkFoo(x) = mkFoo(world, x) # substitute from @with_context declaration
Of course, it’s very quickly became rather complicated: what should we do if we have more than one context? How can we decide which functions to use? And what can we do with nested @in_context
?
So it looks like that building full scaled implementation in this approach is too complicated, but limited versions could exist.