missionary 2025-11-21

Missionary puzzle of the day 🧵

Assuming input as sets

(require '[clojure.set :as set])

(defn differentiate [zero subtract]
  (let [state (object-array 1)]
    (aset state 0 zero)
    (fn [curr]
      (let [prev (aget state 0)]
        (aset state 0 curr)
        (subtract curr prev)))))

(defn set-diffs [x y]
  (-> {}
    (into (map (juxt identity (constantly true))) (set/difference x y))
    (into (map (juxt identity (constantly false))) (set/difference y x))))

(defn set-events [flow-of-sets]
  (m/ap (m/?> (m/seed ((differentiate {} set-diffs) (m/?> flow-of-sets))))))

(defn reconcile-merge [flow-of-sets-of-flows]
  (m/ap (let [[flow lifecycle] (m/?> ##Inf (m/group-by key (set-events flow-of-sets-of-flows)))]
          (try (if (m/?< (m/eduction (map val) (take 2) lifecycle))
                 (m/?> flow) (m/amb))
               (catch Cancelled _ (m/amb))))))

(comment

  (defn inner-flow [id]
    (m/observe
      (fn [!]
        (prn id :up)
        #(prn id :down))))

  (def flow-1 (inner-flow 1))
  (def flow-2 (inner-flow 2))
  (def flow-3 (inner-flow 3))
  (def flow-4 (inner-flow 4))

  (def !active (atom #{flow-1}))

  (def ps
    ((m/reduce (fn [_ x] (prn x)) nil
       (reconcile-merge (m/watch !active)))
     prn prn))
  ;; 1 :up

  (reset! !active #{flow-1})

  (reset! !active #{flow-1 flow-2 flow-3})
  ;; 2 :up
  ;; 3 :up

  (reset! !active #{flow-2 flow-3})
  ;; 1 :down

  (reset! !active #{flow-2 flow-4})
  ;; 3 :down
  ;; 4 :up
  )

How cool! Trying it out. Thanks as always.

Flow A is a flow of flows:

t0 [flow-1]
t1 [flow-1]
t2 [flow-1 flow-2 flow-3]
t3 [flow-2 flow-3]
t4 [flow-2 flow-4]
I'm trying to write a function (run flow-a). At each emit of Flow A, I want run to start any added flows, and stop any removed flows:
t0 flow-1                      ;; flow 1 starts
t1   |                         ;; 
t2 flow-1 flow-2 flow-3        ;; flows 2 and 3 start
t3          |    flow-3        ;; flow 1 stops
t4        flow-2        flow-4 ;; flow 3 stops; flow 4 starts
t5                             ;; flows 2 and 4 stop
Crucially, I don't want to restart flows at every emit. Values emitted from the internal flow-1 through flow-4 should be merged together into run's output flow.

Been scratching my head on this one for a little bit

I feel like the secret is group-by

But I can't quite wrap my head around it

(Notes to self for monday) I think the solution to this problem is to create: 1. A map m mapping unique identities to flows 2. A function taking m and a flow of active-identities that, for each emission: a. Starts flows associated with IDs in the current emit, but not in the previous one b. Stops flows associated with IDs in the previous emit, but not in the current one 3. Probably some kind of combine argument that describes how to mix elements from activated flows