c42f / fastclosures.jl Goto Github PK
View Code? Open in Web Editor NEWFaster closure variable capture
License: Other
Faster closure variable capture
License: Other
using FastClosures
outer = [[1]]
@closure map(outer) do inner
map(inner) do x
x + 1
end
end
UndefVarError: x not defined
The reason is, that @closure
thinks x
is a variable from outer scope:
quote
let x = x
map(outer) do inner
map(inner) do x
x + 1
end
end
end
end
julia> using FastClosures
INFO: Precompiling module FastClosures.
ERROR: LoadError: syntax: extra token "Symbol" after end of expression
in include_from_node1(::String) at ./loading.jl:488
in include_from_node1(::String) at /usr/local/Cellar/julia/0.5.2/lib/julia/sys.dylib:?
in macro expansion; at ./none:2 [inlined]
in anonymous at ./<missing>:?
in eval(::Module, ::Any) at ./boot.jl:234
in eval(::Module, ::Any) at /usr/local/Cellar/julia/0.5.2/lib/julia/sys.dylib:?
in process_options(::Base.JLOptions) at ./client.jl:242
in _start() at ./client.jl:321
in _start() at /usr/local/Cellar/julia/0.5.2/lib/julia/sys.dylib:?
while loading /Users/dpo/.julia/v0.5/FastClosures/src/FastClosures.jl, in expression starting on line 15
julia> VERSION
v"0.5.2"
julia> f(x) = @closure i -> @inbounds x[i]
f (generic function with 1 method)
julia> f([])
ERROR: UndefVarError: pop not defined
Stacktrace:
[1] macro expansion at /home/takafumi/.julia/packages/FastClosures/mMjMm/src/FastClosures.jl:51 [inlined]
[2] f(::Array{Any,1}) at ./REPL[37]:1
[3] top-level scope at REPL[38]:1
Maybe @closure
needs to exclude :inbounds
node when accumulating the variables?
julia> @macroexpand @closure i -> @inbounds x[i]
quote
#= /home/takafumi/.julia/packages/FastClosures/mMjMm/src/FastClosures.jl:51 =#
let x = x, pop = pop
#= /home/takafumi/.julia/packages/FastClosures/mMjMm/src/FastClosures.jl:52 =#
i->begin
#= REPL[40]:1 =#
begin
$(Expr(:inbounds, true))
local #215#val = x[i]
$(Expr(:inbounds, :pop))
#215#val
end
end
end
end
@closure
doesn't like this:
@closure x -> begin
y::Float64 = 0.0
y += x
return y
end
results in
ERROR: syntax: type of "y" declared in inner scope
@macroexpand
shows the above get's turned into
let y = y, Float64 = Float64
x->begin
# ...
end
end
I guess there's in principle no reason to disallow things like y::Float64
in a closure, though, right?
@JuliaRegistrator register()
"""
@closure1 e::Expr
Applies closure to the first argument of a function. This is useful for do-block
syntax.
"""
macro closure1(e::Expr)
if e.head != :call
error("Must be a function call")
end
e.args[2] = :($FastClosures.@closure $(e.args[2]))
esc(e)
end
Hi,
Was the plan for this package to get registered?
What needs to be done to get there?
Thanks,
Josh
julia> @closure function (a)
a
end
ERROR: AssertionError: ex isa Expr && ex.head == Symbol("->")
But for me, no need to fix this as
julia> @closure (a) -> begin
a
end
(::#100) (generic function with 1 method)
works.
Is there a reason why this is disallowed?
@closure function (x,y,z)
x+a
end
It stems from this assert here:
FastClosures.jl/src/FastClosures.jl
Line 40 in f931420
Happy to contribute a PR to handle this case, but just wanted to check if there's a reason for that assert. Seems related to #1.
In Julia 0.6.3, I get
julia> using FastClosures
julia> A = rand(5,5);
julia> h(f1, f2, x) = f1(x) + f2(x)
h (generic function with 1 method)
julia> h(@closure x -> A * x, @closure x -> transpose(A) * x, rand(5))
ERROR: ArgumentError: Argument to @closure must be a closure! (Got ((x->begin # REPL[8], line 1:
transpose(A) * x
end), rand(5)))
Everything works if I first define
julia> f1 = @closure x -> A * x
(::#7) (generic function with 1 method)
julia> f2 = @closure x -> transpose(A) * x
(::#9) (generic function with 1 method)
julia> h(f1, f2, rand(5)) # all ok!
The last example seems to work now without @closure
julia> versioninfo()
Julia Version 1.7.3
Commit 742b9abb4d (2022-05-06 12:58 UTC)
Platform Info:
OS: Linux (x86_64-pc-linux-gnu)
CPU: 11th Gen Intel(R) Core(TM) i7-11700KF @ 3.60GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-12.0.1 (ORCJIT, icelake-client)
Environment:
JULIA_NUM_THREADS = 16
julia> using FastClosures
julia> # code_warntype problem
function f1()
if true
end
r = 1
cb = ()->r
identity(cb)
end
f1 (generic function with 1 method)
julia> # code_warntype clean
function f2()
if true
end
r = 1
cb = @closure ()->r
identity(cb)
end
f2 (generic function with 1 method)
julia> @code_warntype f1()
MethodInstance for f1()
from f1() in Main at REPL[42]:2
Arguments
#self#::Core.Const(f1)
Locals
#16::var"#16#17"{Int64}
cb::var"#16#17"{Int64}
r::Int64
Body::var"#16#17"{Int64}
1 ─ Core.NewvarNode(:(#16))
│ Core.NewvarNode(:(cb))
│ Core.NewvarNode(:(r))
└── goto #2 if not true
2 ─ (r = 1)
│ %6 = Main.:(var"#16#17")::Core.Const(var"#16#17")
│ %7 = Core.typeof(r::Core.Const(1))::Core.Const(Int64)
│ %8 = Core.apply_type(%6, %7)::Core.Const(var"#16#17"{Int64})
│ (#16 = %new(%8, r::Core.Const(1)))
│ (cb = #16::Core.Const(var"#16#17"{Int64}(1)))
│ %11 = Main.identity(cb::Core.Const(var"#16#17"{Int64}(1)))::Core.Const(var"#16#17"{Int64}(1))
└── return %11
julia> @code_warntype f2()
MethodInstance for f2()
from f2() in Main at REPL[43]:2
Arguments
#self#::Core.Const(f2)
Locals
cb::var"#18#19"{Int64}
r@_3::Int64
#18::var"#18#19"{Int64}
r@_5::Int64
Body::var"#18#19"{Int64}
1 ─ Core.NewvarNode(:(cb))
│ Core.NewvarNode(:(r@_3))
└── goto #2 if not true
2 ─ (r@_3 = 1)
│ %5 = r@_3::Core.Const(1)
│ (r@_5 = %5)
│ %7 = Main.:(var"#18#19")::Core.Const(var"#18#19")
│ %8 = Core.typeof(r@_5::Core.Const(1))::Core.Const(Int64)
│ %9 = Core.apply_type(%7, %8)::Core.Const(var"#18#19"{Int64})
│ (#18 = %new(%9, r@_5::Core.Const(1)))
│ (cb = #18::Core.Const(var"#18#19"{Int64}(1)))
│ %12 = Main.identity(cb::Core.Const(var"#18#19"{Int64}(1)))::Core.Const(var"#18#19"{Int64}(1))
└── return %12
The semantics of variable capture in @closure
were originally intended to be the same as julia, but I got this wrong because at the time I didn't understand the intended semantics of the core language which is that the value associated with a name may be changed either within the closure or within the surrounding function and in both of these cases the change must be dynamically reflected in the other scope.
In particular, the value of x
in the following is modified outside the closure, followed by returning closure, and there's nothing @closure
can know about this:
using FastClosures
function foo()
x = 10
c = ()->x
x = 20
c()
end
function bar()
x = 10
c = @closure ()->x
x = 20
c()
end
julia> foo()
20
julia> bar() # @closure breaks this!
10
On the other hand, @closure
does quite a bit of work to allow the following pattern to work the same as in the core language:
function baz()
x = 10
c = ()->(x = 20)
c()
x
end
function qux()
x = 10
c = @closure ()->(x = 20)
c()
x
end
julia> baz()
20
julia> qux()
20
(Though x+=10
doesn't appear to work... I guess that's a bug. Ugh!)
In hindsight I think this makes the semantics @closure
pretty confusing and it would be better to just freeze all bindings inside the closure to fix them to the values they had at closure creation time. See also some discussion in #5 (CC @bramtayl)
Some possible names for such a macro...
@capturevalues # semantically questionable ?
@freeze_captures
@freeze
@rebind
@constbind # questionable
@newbindings
@let # too technical ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.