Обновление 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}}]}