I would suggest that you (1) write the function to take all of the arguments it actually needs and then (2) use closures to fix some subset of those arguments when you need to do something like compute a gradient.
For example:
julia> function f(x, y, z)
z * (x + y)
end
f (generic function with 2 methods)
julia> g = (x, y) -> f(x, y, 2)
#7 (generic function with 1 method)
julia> g(1, 2)
6
Here f
takes three arguments, all the data it requires to perform its operations. But given some fixed z
(2 in this case), we can create a new anonymous function of just x
and y
that calls f
as needed.
In this way, all of the function arguments are clearly indicated and efficiently passed into f
, but we can still use g
as the two-argument function you’re looking for.