Suppose you have two values x
and y
such that y
should be computed every time x
is updated. In Java, everything is an object so I have little choice. The way to do it would be via a class, e.g.
public Data {
private type x, y;
Data(type x) {
this.x = x;
this.y = null;
}
void updateX(type2 val) {
// perform operation to update X
// perform (expensive) operation to update Y
}
void getX() {...}
void getY() {...}
}
In clojure, a direct translation of this might use deftype
and defprotocol
/definterface
. But that's too much for just defining a dependency between two variables. In particular, I have an objection to giving it a name and treating it on par with actual types and protocols.
I know this is what defrecord
s are for -- when you need to make a class that does not represent an entity in the business domain, but then defrecord
s are immutable.
Do I have other choices? I had a look at RxClojure
because the situation here seems to be "reactive", but that seems to be oriented toward "emit"-ing events rather than e.g. just storing the latest one in an atom.
you could also use add-watch facility for that. For example:
(defn entangle [x-val computation]
(let [x (atom x-val)
y (atom (computation x-val))]
(add-watch x nil (fn [_ _ old-x new-x]
(reset! y (computation new-x))))
{:x x
:y y}))
In the above example y
value depends on x
value through the computation
function.
(let [{:keys [x y]} (entangle 10 #(* % %))]
(println "x:" @x "y:" @y)
(swap! x inc)
(println "x:" @x "y:" @y)
(reset! x 200)
(println "x:" @x "y:" @y))
;; x: 10 y: 100
;; x: 11 y: 121
;; x: 200 y: 40000
nil
This could be handy in some situations, though generally such tangled code eliminates referential transparency, and i would probably avoid using it.
This is exactly what I was looking for. I agree that this could be confusing, but I think the confusion could be managed (certainly not eliminated) by putting both the refs in an object (as you do at the return of
entangle
), and users of the code being aware that this is a special "reactive" object, perhaps not even trying to separate the refs into different variables as you did in thelet
.