Clojure alter update-in возвращает nil, а dosync не позволяет повторяться

РЕДАКТИРОВАТЬ:

dosync сам создает функцию, поэтому вызовы recur интерпретируются как вызовы, сделанные сгенерированной dosync функцией.

Это след функции, которую я на самом деле сделал. Сделал как можно проще, я думаю.

(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))))

Исключение:

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

ОРИГИНАЛ:

Я пытался обновить хэш-карту в ссылке следующим образом

(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

Что было довольно удивительно, так как он должен был вернуться

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

Итак, чем я пытался:

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

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

Но потом я прочитал, что alterзвонит apply так:

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

user=> {:some nil}

Итак, давайте сделаем это правильно:

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

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

Хорошо, что я понял это, но это не объясняет, почему в alter что-то идет не так, и я все равно не могу это изменить...

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

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

Это выглядит ужасно, но, по крайней мере, это работает, и я могу симулировать это на alter :D

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

user=> nil

Хорошо, ты выиграл.

Я просмотрел: clojure.lang .Ref.alter source, но ничего не изменилось. (кроме того, насколько я понимаю, alter на самом деле не звонит apply)

Я надеюсь, что некоторые из вас поймут это и ответят, какой код является правильным.


person user5211470    schedule 05.10.2016    source источник
comment
Что вы имеете в виду, когда используете recur внутри dosync? Вам действительно нужны вложенные транзакции?   -  person OlegTheCat    schedule 07.10.2016
comment
@OlegTheCat На самом деле да, у меня есть вложенная структура ссылок, и я хочу найти последнюю в пути, поэтому я могу ее изменить, проблема решается, кстати, просто путем проверки наличия вызова функции в dosync.   -  person user5211470    schedule 07.10.2016
comment
@OlegTheCat, кстати, я согласен с вашим вопросом, если бы я запускал транзакцию, а затем повторял ее, это было бы плохой идеей, поскольку транзакция всегда одна (в хвостовой позиции). Я думаю, это нормально, может быть, было бы лучше, если бы Я сделал отдельную чистую функцию для поиска по пути   -  person user5211470    schedule 07.10.2016


Ответы (1)


Проблема в том, что подпись sync выглядит следующим образом:

(флажки синхронизации игнорируются на данный момент и тело)

Первый аргумент этого макроса игнорируется. Также документация рекомендует передавать для него nil:

транзакционные флаги => TBD, ноль пока

Итак, правильный способ использования sync:

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

Я бы также рекомендовал использовать dosync вместо sync (просто не связывайтесь с первым параметром; по сути, эти функции /core.clj#L4880" rel="nofollow">одинаковые):

> (dosync (alter example-ref update-in [:some] (partial conj [])))
{:some [{:nested {:structure :foo}}]}
person OlegTheCat    schedule 05.10.2016
comment
На самом деле я использовал dosync раньше, но это выдавало ошибку о вызове recur с неправильным количеством аргументов, читал, что dosync создает анонимную функцию, вы знаете, как это обойти? - person user5211470; 05.10.2016
comment
Можете ли вы отредактировать свой вопрос и указать MCVE, который вызывает проблему с dosync? - person OlegTheCat; 05.10.2016