I want to know how to wrap a function (or function definition) such that it becomes seemingly agnostic to whether the parameters passed to it are mutable or immutable -- but if any parameter it is given is mutable, it should dereference that parameter every time it is called, to get the current value.
I could write a function that requires each parameter to be mutable storage that it then dereferences each time it is called. But there's a performance hit (very, very small, I know!) to dereferencing mutable storage in Clojure. In my specific use case these actually are bottleneck operations, small enough for dereferencing to make a difference, and repeated hundreds of thousands to millions of times (more on my use case below, but for now let's just assume this is significant). So I don't want to use mutable data in the cases where I don't need it to be mutable. It would be nice if, from the outside, the code appeared not to care whether the initial parameters were mutable or immutable. Let's say, for simplicity's sake, that the function is the following:
(defn foo [a b]
(fn [x] (* a b x)))
(def instance (foo 3 4))
(instance 5) ; <- 60
(instance 8) ; <- 96
I would like a foo
that is smart enough to do this:
(def a (agent 3))
(def b (agent 4))
(foo a b) ; <- (fn [x] (* (deref a) (deref b) x))
(foo a 4) ; <- (fn [x] (* (deref a) 4 x))
(foo 3 4) ; <- (fn [x] (* 3 4 x))
However, my first attempt to do something used quoting and unquoting (natural, right? It's what macros use!), and it gave me a nasty error about embedding objects in code (a very similar issue, different use-case, is discussed here). My next attempt gave me a weird (and massive) slowdown in runtimes.
Does anyone know of a good way to do this?
Background
I am working some machine learning algorithms. In a typical scenario, the user would initialize an algorithm with a certain set of parameters, and then run it on a set of data. But sometimes a user/user-defined code might want to modify the parameters as the algorithm is running, either based on time (e.g., simulated annealing), or based on some other criteria determined while watching the algorithm's ongoing performance. My algorithms are parallelized, and each thread would need to see the change. Restarting the algorithm when I'm changing the parameters would defeat the purpose.