julia> foo = () -> 3
#1 (generic function with 1 method)
julia> bar() = 3
bar (generic function with 1 method)
julia> foo()
3
julia> bar()
3
help?> foo
search: foo floor pointer_from_objref OverflowError RoundFromZero unsafe_copyto! functionloc StackOverflowError
No documentation found.
foo is a Function.
# 1 method for anonymous function "#1":
[1] (::var"#1#2")() in Main at REPL[1]:1
help?> bar
search: bar baremodule SubArray GlobalRef clipboard BitArray backtrace BitMatrix catch_backtrace AbstractRange
No documentation found.
bar is a Function.
# 1 method for generic function "bar":
[1] bar() in Main at REPL[2]:1
The only difference is that (::var"#1#2") appears in the help description where I would expect to see foo.
What is the purpose of a construction like evalcb = () -> @show loss(tx, ty)?
They are different objects with different behaviour in regards to optimization, scope rules and so on.
Compare these two definitions
function f1(x)
if x == 1
g() = 3
else
g() = 4
end
return g
end
function f2(x)
if x == 1
g = () -> 3
else
g = () -> 4
end
return g
end
julia> f1(1)()
4
julia> f2(1)()
3
In the first function g is defined only once (probably you may say it is “global” to the scope of the function). But in the second function these are two different and independent objects.
It probably does not explain why in this particular example of the Flux.jl the second form was chosen, but probably due to the nature of the operations that happen in Flux.jl it is safer to use it always, so you wouldn’t run into some unexpected corner cases.
I think it’s because Julia evaluates g() = 3 and then overrides it by evaluating g() = 4 (even if the condition is not hit). So regardless of the value of x, f1(x)() will call g(), which will always return 4.
Glad I’m not the only one baffled by the behavior of your f1. I have gotten away with the assumption that if an if condition is met, then the material inside of an else condition will never have any effect.
Just for completeness, another small difference is how they’re serialized. For the anonymous version, the function definition is actually serialized, where as the non-anonymous one just the name is serialized. The niche place where this might come up is when using Distributed’s auto-global shipping, so e.g. this works,
foo = () -> 3
@fetch foo() # foo definition shipped to worker, run there, answer returned
whereas the non-anonymous version you need to have foo actually defined on all workers (since only its name is serialized), which ammounts to an @everywhere
I think one should not do that without reason. It declares a global variable and there will be an overhead in calling it in respect to a declared function.