Обновление Clojure alter возвращает 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, но не стал ни мудрее. (кроме того, что, насколько я понимаю, alter на самом деле не звонит apply)

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

1 ответ

Проблема в том, что подпись sync как следует:

(синхронизировать флаги, игнорируемые на время и тело)

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

транзакции-флаги => TBD, передайте ноль сейчас

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

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

Я также рекомендую использовать dosync вместо sync (просто не связываться с первым параметром; по сути, эти функции одинаковы):

> (dosync (alter example-ref update-in [:some] (partial conj [])))
{:some [{:nested {:structure :foo}}]}
Другие вопросы по тегам