; feed/stream — a stream is an APL vector (rank-1 array) whose ravel holds ; activity dicts. Operations lift APL primitives onto this shape: filter via ; compress (/), sort via grade (⍋), take via ↑, reverse via ⌽. ; ; Requires: lib/apl/runtime.sx, lib/feed/normalize.sx (loaded by harness). (define feed/stream (fn (acts) (make-array (list (len acts)) acts))) (define feed/items (fn (s) (get s :ravel))) (define feed/count (fn (s) (len (get s :ravel)))) (define feed/empty (feed/stream (list))) (define feed/empty? (fn (s) (= (feed/count s) 0))) ; filter — bool mask ∘ compress. pred : activity -> truthy (define feed/filter (fn (s pred) (let ((items (get s :ravel))) (let ((mask (make-array (list (len items)) (map (fn (a) (if (pred a) 1 0)) items)))) (apl-compress mask s))))) ; sort-by — ascending, stable on ties (grade-up is stable). key-fn : activity -> number (define feed/sort-by (fn (s key-fn) (let ((items (get s :ravel))) (let ((keys (make-array (list (len items)) (map key-fn items)))) (let ((order (get (apl-grade-up keys) :ravel))) (feed/stream (map (fn (i) (nth items (- i 1))) order))))))) (define feed/sort-by-at (fn (s) (feed/sort-by s feed/at))) ; newest-first: ascending sort then reverse (⌽) (define feed/recent (fn (s) (apl-reverse (feed/sort-by-at s)))) ; take N (↑), clamped to stream length so it never over-takes/pads (define feed/take (fn (s n) (let ((c (feed/count s))) (if (>= n c) s (apl-take (apl-scalar n) s))))) (define feed/reverse (fn (s) (apl-reverse s))) ; common predicates (define feed/by-actor (fn (s actor) (feed/filter s (fn (a) (equal? (get a :actor) actor))))) (define feed/by-verb (fn (s verb) (feed/filter s (fn (a) (equal? (get a :verb) verb))))) (define feed/by-object (fn (s object) (feed/filter s (fn (a) (equal? (get a :object) object))))) ; activities at or after timestamp t (define feed/since (fn (s t) (feed/filter s (fn (a) (>= (get a :at) t)))))