#clojure #hashmap #apply #stm #update-in
#clojure #hashmap #применить #stm #обновление
Вопрос:
Редактировать:
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
ОРИГИНАЛ:
Я пытался обновить hashmap в ссылке следующим образом
(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
so:
(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
)
Я надеюсь, что некоторые из вас поймут это и получат ответ относительно того, какой код является правильным.
Комментарии:
1. Что вы имеете в виду при использовании
recur
внутриdosync
? Вы действительно хотите, чтобы там были вложенные транзакции?2. @OlegTheCat Я на самом деле да, у меня есть вложенная структура ссылок, и я хочу найти последнюю в пути, поэтому я не меняю ее, проблема решена, кстати, просто убедившись, что в dosync есть вызов функции.
3. @OlegTheCat кстати, я согласен с вашим вопросом, если бы я запустил транзакцию, а затем повторил, это было бы не очень хорошей идеей, поскольку транзакция всегда является единственной (в хвостовой позиции) Я думаю, что это нормально, может быть, было бы лучше, если бы я создал отдельную чистую функцию для поиска в пути
Ответ №1:
Проблема в том, что подпись sync
выглядит следующим образом:
(флаги синхронизации -игнорируются-пока и тело)
Первый аргумент этого макроса игнорируется. Также документация рекомендует пройти nil
для этого:
transaction-flags => TBD, пока передайте nil
Итак, правильный способ использования 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}}]}
Комментарии:
1. На самом деле я использовал dosync раньше, но это выдавало ошибку о повторном вызове с неправильным количеством аргументов, прочитал, что dosync создает анонимную функцию, вы знаете, как это обойти?
2. Можете ли вы отредактировать свой вопрос и указать MCVE, который вызывает проблему
dosync
?