For each type T you use there will be one type of Adder{T}. The function factory strategy will create different closures (each with a different type) each time you run it, even with the same value. It seems to me, therefore, that the function factory strategy overspecializes. I was wrong about this, see @rdeits answer below. If the closures come all from the same factory (i.e., outer function) they will have the same type for the same type of captured variables (if the compiler infer the types right, see the second point). So, for the examples above it would be the same. The struct approach can give you a finer control over specialization, as you can decide how much you want to parametrize the struct type over the captured variables, and you know the type is the same being built inside any function.
There are some performance problems with closures, while I am not sure if they would affect your specific case: https://github.com/JuliaLang/julia/issues/15276#issuecomment-628844489 Basically, closures often lose type information of captured bindings and end up boxing them unnecessarily.
You can use a mutable struct to change the boxed value if you need it, and if you do not then you can use an immutable struct which (I think) it is more lightweight than a closure EDIT: seems like they are about the same, as closures lower to anonymous immutable structs.
This is not actually true, and it’s easy to verify that with the example from the OP:
julia> function createAdder(a)
function adder(b)
a+b
end
end
createAdder (generic function with 1 method)
julia> plus4 = createAdder(4)
(::var"#adder#5"{Int64}) (generic function with 1 method)
julia> plus5 = createAdder(5)
(::var"#adder#5"{Int64}) (generic function with 1 method)
julia> typeof(plus4) == typeof(plus5)
true
The closure approach creates a new type only when you define the createAdder function, so the number of types created is exactly the same as the struct approach.
I also benchmarked both approaches for my problem in question now and and the differences are negligible. So as far as I can tell it is up to personal taste. I will stick with the struct-based approach for now
To add to rdeits excellent response: If I understand correctly, closures in julia are implemented with the struct approach you have described above (there are other possible implementations). So expect no fundamental difference…
100% with one caveat: accessing the fields of a closure is not technically a stable part of the language’s API, so if you need to access the a field then you should use the explicit struct approach since that is guaranteed to work no matter what changes are made to the compiler. If you don’t need to access the a field then it’s just a matter of preference.
The performance problem with closures are not something that can be avoided using the “explicit” struct approach? In other words, sometimes using structs explicitly the programmer cannot end being smarter than the current closure lowering approach?
Yes, in those cases a typed mutable struct may end up being more efficient because as I understand it the compiler is not that great yet at limiting the type of “boxed” variables in the closure object.