0

EDIT:

dosync creates itself a function so calls to recur get interpreted as calls made to the function dosync generated.

This is the footprint of the function I actually made. Kept it as simple as possible, I think.

(defn change-to-ref [ref data]
  (dosync
    (if-let [[new-ref new-data] (some-test @ref data)]
      (recur new-ref new-data)
      (alter ref f data))))

The Exception:

CompilerException java.lang.IllegalArgumentException:
Mismatched argument count to recur, expected: 0 args, got: 2

ORIGINAL:

I was trying to update a hashmap in a ref as follows

(def example-ref (ref {:some {:nested {:structure}}}))
(defn f [this] [this])  ;; just an example function

(sync (alter example-ref update-in [:some] f)

user=> nil

Which was quite surprising as it should've returned

user=> {:some [{:nested {:structure}}]}

So than I tried:

(update-in @example-ref [:some] f)

user=> {:some [{:nested {:structure}}]}

But than I read that altercalls apply so:

(apply update-in @example-ref '([:some] f))

user=> {:some nil}

Okay, so lets do it the proper way:

(apply update-in @example-ref (list [:some] f))

user=> {:some [{:nested {:structure}}]}

Well it's great I figured this out, but it doesn't explain why this goes wrong in alter and I can't even change it anyways...

(apply (fn [a b] (update-in a b f)) @example-ref '([:something]))

user=> {:some [{:nested {:structure}}]}

It looks terrible but atleast it works and I can simulate it for alter :D

(sync (alter example-ref (fn [a b] (update-in a b f)) [:some])

user=> nil

Ok, you win.

I took a look at: clojure.lang.Ref.alter source but have grown none the wiser. (other than, that to my understanding, alter actually doesn't call apply)

I hope some of you will understand this and have an answer as to what is the proper code.

  • What do you mean when using `recur` within `dosync`? Do you really want nested transactions there? – OlegTheCat Oct 07 '16 at 08:50
  • 1
    @OlegTheCat I actually do yes, I have a nested structure of refs and want to find the last one within a path so I van change it, the problem is resolved btw simply by making sure theres a function call within dosync. – user5211470 Oct 07 '16 at 09:15
  • @OlegTheCat btw I agree with your question if I would run a transaction and then recur, that wouldn't be a good idea, since the transaction always is a single one (in tail position) I think this is fine, maybe its nicer if I made a seperate pure function for searching in a path – user5211470 Oct 07 '16 at 09:22

1 Answers1

0

The problem is that signature of sync is as follows:

(sync flags-ignored-for-now & body)

First argument of this macro is ignored. Also documentation recommends to pass nil for it:

transaction-flags => TBD, pass nil for now

So, correct way of using sync would be:

> (sync nil (alter example-ref update-in [:some] (partial conj [])))
{:some [{:nested {:structure :foo}}]}

I'd also recommend to use dosync instead of sync (just not to mess with the first parameter; essentially these functions are the same):

> (dosync (alter example-ref update-in [:some] (partial conj [])))
{:some [{:nested {:structure :foo}}]}
OlegTheCat
  • 4,443
  • 16
  • 24
  • Actually I used dosync before, but that gave an error about recur being called with the wrong amount of args, read that dosync makes an anonymous function, do you know how to get around this? – user5211470 Oct 05 '16 at 16:45
  • Can you edit your question and put MCVE that causes the issue with `dosync`? – OlegTheCat Oct 05 '16 at 16:57