; feed/rank — scoring + ranking. Scorers are (activity -> number). Ranking is a ; stable two-pass grade-down: first by :at descending (the tiebreak), then by ; score descending — so ties resolve by recency, then by input order. Fully ; deterministic on ties. ; ; Requires: lib/apl/runtime.sx, lib/feed/normalize.sx, lib/feed/stream.sx. ; --- scorers ---------------------------------------------------------------- ; recency: half-life decay. score = 0.5 ^ (age / half-life). at==now -> 1.0. (define feed/recency (fn (now half-life) (fn (a) (expt 0.5 (/ (- now (get a :at)) half-life))))) ; velocity: how many of this actor's activities fall in (at-window, at] — ; a burst of recent activity scores higher. (define feed/velocity (fn (stream window) (fn (a) (len (filter (fn (b) (and (equal? (get b :actor) (get a :actor)) (<= (get b :at) (get a :at)) (> (get b :at) (- (get a :at) window)))) (feed/items stream)))))) ; engagement: how many activities in the stream touch this activity's :object (define feed/engagement (fn (stream) (fn (a) (len (filter (fn (b) (equal? (get b :object) (get a :object))) (feed/items stream)))))) ; composite: weighted sum. parts = (list (list weight scorer) ...) (define feed/composite (fn (parts) (fn (a) (reduce (fn (acc p) (+ acc (* (first p) ((nth p 1) a)))) 0 parts)))) ; --- ranking ---------------------------------------------------------------- ; stable reorder of items by key-fn, descending (grade-down is stable) (define feed/-desc-by (fn (items key-fn) (let ((keys (make-array (list (len items)) (map key-fn items)))) (let ((order (get (apl-grade-down keys) :ravel))) (map (fn (i) (nth items (- i 1))) order))))) ; rank by score descending; ties -> :at descending -> input order (define feed/rank (fn (stream score-fn) (let ((by-at (feed/-desc-by (feed/items stream) feed/at))) (feed/stream (feed/-desc-by by-at score-fn))))) ; attach a :score to each activity (for inspection / debugging) (define feed/with-scores (fn (stream score-fn) (feed/stream (map (fn (a) (assoc a :score (score-fn a))) (feed/items stream))))) ; top-N ranked timeline (define feed/top (fn (stream score-fn n) (feed/take (feed/rank stream score-fn) n)))