Warm tip: This article is reproduced from serverfault.com, please click

Standard practice for the pattern of updating a field when another one updates

发布于 2020-11-28 11:31:08

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 defrecords are for -- when you need to make a class that does not represent an entity in the business domain, but then defrecords 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.

Questioner
Peeyush Kushwaha
Viewed
0
leetwinski 2020-11-30 03:47:37

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.