Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 21s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
115 lines
3.2 KiB
Plaintext
115 lines
3.2 KiB
Plaintext
; feed/fanout — THE SHOWCASE. Fan activities out to followers via the APL outer
|
||
; product (∘.×). activities ∘.× audience → an (activity × follower) matrix of
|
||
; inbox events; flatten to a vector; guard-keep only real follow edges.
|
||
;
|
||
; Requires: lib/apl/runtime.sx, lib/feed/normalize.sx, lib/feed/stream.sx.
|
||
;
|
||
; NOTE: apl-outer's combiner result is run through (if (scalar? r) (disclose r) r).
|
||
; A bare dict counts as a scalar (shape ()) and disclose nils it — so the combiner
|
||
; must (enclose ...) its event dict; apl-outer then discloses it back intact.
|
||
|
||
; --- graph: {followee -> (list of followers)} -------------------------------
|
||
|
||
(define feed/followers (fn (graph user) (get graph user (list))))
|
||
|
||
; build a graph from (follower followee) edges: "follower follows followee"
|
||
(define
|
||
feed/follow-graph
|
||
(fn
|
||
(edges)
|
||
(reduce
|
||
(fn
|
||
(g e)
|
||
(let
|
||
((follower (first e)) (followee (nth e 1)))
|
||
(assoc
|
||
g
|
||
followee
|
||
(append (feed/followers g followee) (list follower)))))
|
||
{}
|
||
edges)))
|
||
|
||
; --- helpers ----------------------------------------------------------------
|
||
|
||
; unwrap an apl-scalar (has :ravel) back to its value; pass activities through
|
||
(define
|
||
feed/-val
|
||
(fn
|
||
(x)
|
||
(if (and (= (type-of x) "dict") (has-key? x :ravel)) (disclose x) x)))
|
||
|
||
(define feed/-elem? (fn (x lst) (some (fn (y) (equal? x y)) lst)))
|
||
|
||
(define
|
||
feed/-distinct
|
||
(fn
|
||
(lst)
|
||
(if
|
||
(= (len lst) 0)
|
||
(list)
|
||
(get (apl-unique (make-array (list (len lst)) lst)) :ravel))))
|
||
|
||
; rank-2 matrix -> rank-1 stream of its ravel
|
||
(define feed/-flatten (fn (arr) (feed/stream (get arr :ravel))))
|
||
|
||
; distinct receivers across the whole graph, sorted for determinism
|
||
; (dict key order is unspecified, so sort to pin audience/recipient ordering)
|
||
(define
|
||
feed/audience
|
||
(fn
|
||
(graph)
|
||
(sort
|
||
(feed/-distinct
|
||
(reduce
|
||
(fn (acc k) (append acc (feed/followers graph k)))
|
||
(list)
|
||
(keys graph))))))
|
||
|
||
; --- the outer product ------------------------------------------------------
|
||
|
||
; one (activity, follower) inbox event, enclosed so apl-outer keeps the dict
|
||
(define feed/-mk-event (fn (a f) (enclose {:activity (feed/-val a) :to (feed/-val f)})))
|
||
|
||
; keep events where :to actually follows the activity's actor
|
||
(define
|
||
feed/-edge?
|
||
(fn
|
||
(graph)
|
||
(fn
|
||
(ev)
|
||
(feed/-elem?
|
||
(get ev :to)
|
||
(feed/followers graph (get (get ev :activity) :actor))))))
|
||
|
||
; fanout — activities ∘.× audience, flatten, guard-keep real edges
|
||
(define
|
||
feed/fanout
|
||
(fn
|
||
(stream graph)
|
||
(let
|
||
((matrix (apl-outer feed/-mk-event stream (feed/stream (feed/audience graph)))))
|
||
(feed/filter (feed/-flatten matrix) (feed/-edge? graph)))))
|
||
|
||
; --- inbox queries ----------------------------------------------------------
|
||
|
||
(define
|
||
feed/inbox-for
|
||
(fn
|
||
(inbox user)
|
||
(feed/filter inbox (fn (ev) (equal? (get ev :to) user)))))
|
||
|
||
(define
|
||
feed/recipients
|
||
(fn
|
||
(inbox)
|
||
(feed/-distinct (map (fn (ev) (get ev :to)) (feed/items inbox)))))
|
||
|
||
; the activities (unwrapped) destined for a user
|
||
(define
|
||
feed/inbox-activities
|
||
(fn
|
||
(inbox user)
|
||
(map
|
||
(fn (ev) (get ev :activity))
|
||
(feed/items (feed/inbox-for inbox user)))))
|