views:

74

answers:

2

I'm working on a GUI application in Swing+Clojure that requires various mutable pieces of data (e.g. scroll position, user data, filename, selected tool options etc.).

I can see at least three different ways of handling this set of data:

Create a ref to a map of all the data:

(def data (ref {
  :filename    "filename.xml" 
  :scroll      [0 0] }))

Create a map of refs to the individual data elements:

(def datamap {
  :filename    (ref "filename.xml") 
  :scroll      (ref [0 0]) }))

Create a separate ref for each in the namespace:

(def scroll (ref [0 0]))    

(def filename (ref "filename.xml"))

Note: This data will be accessed concurrently, e.g. by background processing threads or the Swing event handling thread. However there probably isn't a need for consistent transactional updates of multiple elements.

What would be your recommended approach and why?

+1  A: 

If there are no concurrency - then there are no difference, you know.

But if it can be used from different threads, then first case will be better. Because it guaranties you a consistent state.

splix
thanks and yes: I certainly like the consistency advantages of the first option. Do you think though though that the other options might also have some advantages in terms of coding style, maintainability and/or performance?
mikera
I don't see any advantages of other options
splix
+6  A: 

Firstly, if you end up using either just one piece of mutable state or several independent pieces (independent as in "it makes no sense to ask whether one is consistent with another"), why not go with Atoms rather than Refs? That's a pretty significant reduction of overhead, which might actually matter if you make a lot of updates.

Secondly, storing filename, scroll position etc. in separate Refs (but not Atoms) is fine as long as you design your transactions carefully (i.e. all relevant Refs need to be mentioned, some might need to be ensured etc. -- basically the transaction must be sure to fail if inconsistent state were to arise). Careful design of such transactions might be a waste of effort if you're managing GUI state most of which hardly ever changes (besides scroll position and buffer contents, what is actually likely to change frequently? -- I mean that's something to consider seriously, as the answer should determine the final design). There's any number of scenarios where having multiple reference type objects is preferable to having one, I'm just not sure that managing basic GUI state is one of them. :-)

Note that performing updates to nested structures held in reference type objects is very clean in Clojure, e.g. (using an Atom):

;; assuming that state-o-matic is an Atom which holds a map,
;; which holds the key of :foo, with which a vector is associated,
;; whose first item is another vector, whose first item is a map
;; which holds the key of :bar associated to a number,
;; the following increments said number
(swap! state-o-matic update-in [:foo 0 1 :bar] inc)

See also get-in & assoc-in.

Michał Marczyk
thanks Michal, very interesting! It sounds from this that the best model might actually be a single map inside an atom. I think this satisfies all the concurrency needs and as you say should have less overhead.
mikera
That is very likely to be the case. Also, it's a good point to start from; it makes it easy to write correct code and you can always split things up later should there be a compelling reason to do so.
Michał Marczyk