There's still times when metaprogramming is preferable to currying, such as
when you want to compile the resulting function for efficiency reasons. (At
some point, we should hopefully get much of the same benefit from function
specialization on arguments that are functions, but we're not there yet.)
I might have written your example as
using Base.Meta.quot
function prodFun(P::Vector, f::Vector{Function})
calls = [:( $(quot(fi))(X[$i], $(quot(P[i]))) ) for (i,fi) in enumerate(
f)]
prod = :( *($(calls...)) )
fun = :( X->$prod )
eval(fun)
end
There's a few points that I think are worth to take up:
- It's sometimes useful to create an Expr explicitly, but it's often a
lot easier to use quoted code and interpolate where necessary.
- Your implementation deferred indexing into f and P until runtime. It's
more efficient to extract the values when you build up the AST. I guess
that you did not intend that g should look up the current values in the
vectors f and P anyway?
- When quoting literal values into an AST, you should always quote them
using Base.Meta.quot or equivalent, otherwise you might get unexpected
results. (Especially if the value in question is a Symbol or an Expr.)
- You can use the multi-argument form of the * function to avoid having
to generate a whole chain of multiplication calls.
- eval executes code in the global namespace, so if you do an assignment
(e.g. the function definition in your example), you will pollute the global
namespace. That is probably not what you want in this case. I've used an
anonymous function instead above to avoid this.
If you want to have a generic function instead of an anonymous one, you can
use
function prodFun(P::Vector, f::Vector{Function})
calls = [:( $(quot(fi))(X[$i], $(quot(P[i]))) ) for (i,fi) in enumerate(
f)]
prod = :( *($(calls...)) )
fun = :( let g(X) = $prod; g; end )
eval(fun)
end
which puts the function definition in a let block so that it won't pollute
the global scope.