bsless / clj-fast

Unpredictably faster Clojure
Eclipse Public License 2.0
242 stars 1 forks source link

[WIP] Add update-many, update-many-> macros #30

Closed yuhan0 closed 3 years ago

yuhan0 commented 3 years ago

I tried generalising the logic from the recent assoc-in changes, to allow for arbitrary updates to multiple nested keys.

Not sure if I fully understand functional lenses, so there might be a more elegant way of doing this than adding another updater parameter. All docstrings are WIP.

The proposed API here adds two macros: update-many is like clojure.core/update-in but restricted to single-arity functions, whereas update-many-> treats the "functions" as threading macro forms so they can be inlined at compile time.

;; clojure.core

(-> m
  (update-in [:a :b] f)
  (update-in [:c :d] g x y z))

;; becomes:
(update-many m
  [:a :b] f
  [:c :d] #(g % x y z))
;; or
(update-many-> m
  [:a :b] (f)
  [:c :d] (g ,, x y z))

I also added a unquoting feature which treats the value as something to be assoc'ed: This could of course be achieved with less magic using (constantly newval) or defining something like (defmacro const-> [_ v] v) to be used in the threading macro.

(update-many-> {:xs    [1 2 3 4 5]
                :cube  4
                :assoc "old"}
  ;; threading macro allows for updating in different positions
  [:xs]   (->> (map dec) (take 3))
  [:cube] (as-> it (* it it it))

  ;; using unquote for assoc-like functionality
  [:assoc] ~"new")
;; => {:xs (0 1 2), :cube 64, :assoc "new"}
bsless commented 3 years ago

You have preempted my work on this :) I'm not sure about mixing the semantics with unquoting, feels a bit like -<> if you're familiar I also think update-many-in can be generally forgone and update-in-> is sufficient, given that you do a little parsing I pushed something similar to a branch here https://github.com/bsless/clj-fast/tree/unified-update-in, includes a little cleanup around update-many

yuhan0 commented 3 years ago

Freaky! I guess it was a natural next step in that direction, and an interesting puzzle in any case :)

I agree about the semantics of ~ being iffy, but it was just awkward trying to shove an assoc in the middle of other updates, without resorting to something like that const-> macro.

I'm happy to close this PR and go with your implementation :)

bsless commented 3 years ago

Implementing update-many was the natural next step, our implementations are so similar they can be considered siblings. I think something like const-> is preferable if only because it does not introduce special parsing semantics, but is explicit.

bsless commented 3 years ago

And thank you for the PR and suggestion of assoc-in, it was a fun brain-teaser