3

I have two maps in refs and want to assoc them to each other in one transaction.

My function looks like this:

(defn assoc-two
  [one two]
  (let [newone (assoc @one :two two)
        newtwo (assoc @two :one one)]
    (ref-set one newone)
    (ref-set two newtwo)))

Now i am calling assoc-two like this:

(dosync (assoc-two (ref {}) (ref {})))

Im getting and StackOverflowError at this point.

I also tried this:

(defn alter-two
  [one two]
  (alter one assoc :two two)
  (alter two assoc :one one))

Is there away to do this in way that one has an entry referencing two and vice versa and still being in one transaction?

Finn
  • 198
  • 9

2 Answers2

6

The stack overflow does not occur until you try to print one of the circular references, e.g. at the REPL.

so.core=> (def a (ref {}))
#'so.core/a
so.core=> (def b (ref {}))
#'so.core/b
so.core=> (do (dosync (alter-two a b)) :success)
:success
so.core=> (= b (:two @a))
true
so.core=> (= a (:one @b))
true

Obviously printing a circularly referenced object like this will be problematic, but see this recent question and answer on disabling default printing of contents of reference types

(remove-method print-method clojure.lang.IDeref)

(dosync (alter-two (ref {}) (ref {})))
;=> {:one #<Ref clojure.lang.Ref@7f1f91ac>} 
;   (prints the second ref without attempting to print its circular contents)
Community
  • 1
  • 1
A. Webb
  • 26,227
  • 1
  • 63
  • 95
2

The answer can be found a few posts down, this is simply the REPL trying to print your recursive structure. You have to either remove the print method:

(remove-method print-method clojure.lang.IDeref)

Or add a print method that handles your specific case, this method has to be more specific than the common clojure.lang.IDeref

hardcoder
  • 86
  • 2