-
-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closures in RuntimeGeneratedFunctions #28
Comments
This is complex enough that a simpler reproducer will likely be necessary to narrow it down. |
module Foo
using RuntimeGeneratedFunctions
const RGF = RuntimeGeneratedFunctions
RGF.init(@__MODULE__)
function genclosure(body, mod::Module)
(mod != @__MODULE__) && !isdefined(mod, RGF._tagname) && RGF.init(mod)
RuntimeGeneratedFunction(mod, mod, :(x -> $body))
end
function bar(n::Int; mod = @__MODULE__)
f = genclosure(:(x * $n), mod)
g = x -> 2 * f(x)
g(3)
end
export bar
export genclosure
# also appears to error
macro baz()
quote
n -> bar(n, mod = $__module__)
end
end
end
julia> using .Foo
# OK
julia> bar(3)
18
julia> bar(3, mod=@__MODULE__)
ERROR: MethodError: no method matching generated_callfunc(::RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(:x,), var"#_RGF_ModTag", var"#_RGF_ModTag", (0x346def3e, 0x769edc98, 0x33949a6f, 0xc294c197, 0xcea7fb6f)}, ::Int64)
The applicable method may be too new: running in world age 29602, while current world is 29605.
Closest candidates are:
generated_callfunc(::RuntimeGeneratedFunctions.RuntimeGeneratedFunction{argnames, cache_tag, var"#_RGF_ModTag", id}, ::Any...) where {argnames, cache_tag, id} at none:0 (method too new to be called from this world context.)
generated_callfunc(::RuntimeGeneratedFunctions.RuntimeGeneratedFunction{argnames, cache_tag, Main.Foo.var"#_RGF_ModTag", id}, ::Any...) where {argnames, cache_tag, id} at none:0
Stacktrace:
[1] (::RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(:x,), var"#_RGF_ModTag", var"#_RGF_ModTag", (0x346def3e, 0x769edc98, 0x33949a6f, 0xc294c197, 0xcea7fb6f)})(args::Int64)
@ RuntimeGeneratedFunctions ~/.julia/packages/RuntimeGeneratedFunctions/tJEmP/src/RuntimeGeneratedFunctions.jl:92
[2] (::Main.Foo.var"#3#4"{RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(:x,), var"#_RGF_ModTag", var"#_RGF_ModTag", (0x346def3e, 0x769edc98, 0x33949a6f, 0xc294c197, 0xcea7fb6f)}})(x::Int64)
@ Main.Foo ./REPL[1]:11
[3] bar(n::Int64; mod::Module)
@ Main.Foo ./REPL[1]:12
[4] top-level scope
@ REPL[4]:1
# Calling it again works
julia> bar(3, mod=@__MODULE__)
18 |
Yeah I don't think internal function definitions are allowed. |
Im not sure I understood. Do you think there is a workaround? |
I'm not sure. Make it a RuntimeGeneratedFunction? |
Which function should be runtime generated? genclosure or bar? |
All? I just don't think it's going to work out because generated functions need purity. But maybe you can fake it like that. |
Thanks. It worked. It is hacky but really does the job: module Foo
using RuntimeGeneratedFunctions
const RGF = RuntimeGeneratedFunctions
RGF.init(@__MODULE__)
function closure_gen(mod::Module)
RuntimeGeneratedFunction((@__MODULE__), (@__MODULE__),
:( body -> begin
($mod != @__MODULE__) && !isdefined($mod, RGF._tagname) && RGF.init($mod)
RuntimeGeneratedFunction($mod, $mod, :(x -> $body))
end
))
end
function barr(n::Int; mod = @__MODULE__)
f = (closure_gen(mod))(:(x * $n))
g = x -> 2 * f(x)
g(3)
end
export barr
end |
Great! |
Here's a generic version: function closure_generator(mod::Module)
RuntimeGeneratedFunction((@__MODULE__), (@__MODULE__),
:( expr -> begin
($mod != @__MODULE__) && !isdefined($mod, RGF._tagname) && RGF.init($mod)
RuntimeGeneratedFunction($mod, $mod, expr)
end
))
end The only problem is that in small reproducer it works as expected, in Metatheory.jl tests it still failed.
Couldn't this behaviour be (sort of) integrated in the RGF package? My package tests (a lot, also about accessing values and dispatching methods in external modules in these "closures") are all passing after using this hack, the original issue was solved. |
It probably could be, but I don't know if it needs to be. |
Recall the closures example from GeneralizedGenerated.jl : @gg function h(x, c)
quote
d = x + 10
function g(x, y=c)
x + y + d
end
end
end
h(1, 2)(1) # => 14 I managed to get the same behaviour. ITS REALLY UGLY THO, and I'm not sure if this would hold at all if mechanized. I think it's worth a try. cgen = closure_generator
h = cgen(@__MODULE__)(:( (x, c) -> begin
d = x + 10
cgen(@__MODULE__)( :((x) -> x + $c + $d) ) # this doesnt convince me
end
))
println(h(1,2)(1)) # => 14 |
Well... it's true you can get a certain kind of "closure" by creating an AST and interpolating the captures in there. Like any other use of RuntimeGeneratedFunction this will work in very particular circumstances where you don't need to create the AST more than a few times. But if you do this in a loop you'll quickly leak a lot of memory and may run into #13 or other problems. To be honest, I feel like closure support within a function foo(x)
f = @RuntimeGeneratedFunction(:((y,z)->y+z^2))
# bind `x` to the second slot of RGF `f`
return y -> f(y,x)
end When you're generating an AST, you've got control over whether that AST uses a closure as part of its implementation, so I think it may simply be best to avoid doing that if you want things to work smoothly with this package. |
Could someone rename this issue to "Closures in RGFs" or something? To capture some (lightly edited) comments I made in a slack discussion with @shashi -
|
I am pretty sure OpaqueClosures can be used in generated functions, so perhaps that might be a better solution here? |
Should close SciML#28. Kwargs or `where` parameters might not work, but that's an issue in Julia base.
Should close SciML#28. Kwargs or `where` parameters might not work, but that's an issue in Julia base.
Just encountered an odd bug testing my package
How to reproduce:
Comment lines 7 and 8 in
Metatheory.jl/test/runtests.jl
https://github.com/0x0f0f0f/Metatheory.jl/blob/a273d9ed6b7d88cadaa3d4f66299de3f3649d719/test/runtests.jl#L7-L8
uncomment last argument (cache module) in line 14 in
Metatheory.jl/test/test_while_interpreter.jl
https://github.com/0x0f0f0f/Metatheory.jl/blob/a273d9ed6b7d88cadaa3d4f66299de3f3649d719/test/test_while_interpreter.jl#L14
Am I just initializing/using RGF in the wrong way? (here and also here) I admit that I have done some weird hacks to achieve a behaviour that is partially close to dynamic scoped variable capturing (not technically closures but close to).
The text was updated successfully, but these errors were encountered: