This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-09-03
Channels
- # announcements (11)
- # atom-editor (8)
- # aws (16)
- # babashka (34)
- # beginners (59)
- # calva (32)
- # cider (8)
- # clj-kondo (43)
- # cljs-dev (52)
- # clojure (26)
- # clojure-europe (11)
- # clojure-italy (2)
- # clojure-nl (5)
- # clojure-spec (16)
- # clojure-uk (44)
- # clojurescript (5)
- # core-async (21)
- # cursive (14)
- # datomic (53)
- # figwheel-main (4)
- # fulcro (5)
- # graphql (6)
- # java (3)
- # kaocha (5)
- # leiningen (6)
- # local-first-clojure (1)
- # malli (25)
- # off-topic (40)
- # other-languages (1)
- # pathom (5)
- # pedestal (3)
- # re-frame (4)
- # reitit (2)
- # reveal (8)
- # rum (21)
- # sci (16)
- # shadow-cljs (90)
- # spacemacs (8)
- # tools-deps (10)
- # vrac (6)
- # xtdb (12)
The idea is to have a structure of subscribers on the different parts (different paths) of the db via a hierarchy of hashmaps which list sets of subscribers in its nodes. I call this structure a subscription-tree
.
That is probably not a new thing, I guess reagent has something like that. The part which might be new is to have a subscription-tree
on each node of the compute graph. It means that the action of listening changes on a part of a data is a generalized and homogenous feature in Vrac.
The selection of the right subscribers is fast when done based on a diff. An since each compute node is going to return a diff, everything should work well and efficiently.
Precision: the diff approach is only related to structural changes. Most operations which will be in the compute node (like first-name + last-name -> full-name
) won’t really need it, and will still be written as a classical function implementation which does not output diffs. Those functions will be wrapped to fit in the compute graph.
Thanks to the subscription-tree
, the template below will use zero compute nodes and will feed directly on the client db through this structure:
;; Subscription-tree on the client db:
{:children {:user {:children {:first-name {:subscribers #{1}}
:last-name {:subscribers #{2}}}}}}
;; Subscribing template:
(let [user (:user global)
{:keys [first-name last-name]} user]
[:div first-name " " last-name])
I am going to start implementing a (slow) reference version of the compute graph, as a proof-of-concept of the algorithms and diff-flow.
Here is a bit of the impl:
(def empty-subscription-tree {})
(defn subscribe-on-path [tree path subscriber]
(if (seq path)
(let [[path-element & path-rest] path]
(update-in tree [:children path-element] subscribe-on-path path-rest subscriber))
(update tree :subscribers (fnil conj #{}) subscriber)))
(defn- update-coll-then-dissoc-empty [coll key f & args]
(let [new-val (apply f (get coll key) args)]
(if (seq new-val) ; new-val is assumed to be a collection
(assoc coll key new-val)
(dissoc coll key))))
(defn unsubscribe-from-path [tree path subscriber]
(if (seq path)
(let [[path-element & path-rest] path]
(update-coll-then-dissoc-empty tree :children
update-coll-then-dissoc-empty path-element
unsubscribe-from-path path-rest subscriber))
(update-coll-then-dissoc-empty tree :subscribers disj subscriber)))
;; Testing
#_ (-> empty-subscription-tree
(subscribe-on-path [:user :first-name] 1)
(subscribe-on-path [:user :last-name] 2))
; => {:children {:user {:children {:first-name {:subscribers #{1}}
; :last-name {:subscribers #{2}}}}}}
#_ (-> *1
(unsubscribe-from-path [:user :first-name] 1)
(unsubscribe-from-path [:user :last-name] 2))
; => {}
— End of today’s brainstorming for me.