diff --git a/prospect/core.rkt b/prospect/core.rkt index 3f40d44..87bb08d 100644 --- a/prospect/core.rkt +++ b/prospect/core.rkt @@ -9,6 +9,7 @@ (require "functional-queue.rkt") (require "route.rkt") (require "patch.rkt") +(module+ test (require rackunit)) ;; Events ⊃ Patches ;; Actions ⊃ Events @@ -63,11 +64,25 @@ (define (clean-actions actions) (filter action? (flatten actions))) -(define (deliver-event e pid p w) - (invoke-process pid - (lambda () ((process-behavior p) e (process-state p))) - (lambda (t) (apply-transition pid t w)) - (lambda (exn) (kill-process pid exn w)))) +(define (send-event e pid w) + (match (hash-ref (world-process-table w) pid #f) + [#f w] + [(and p (process _ behavior old-state)) + (invoke-process pid + (lambda () (behavior e old-state)) + (match-lambda + [#f w] + [(transition new-state new-actions) + (update-process pid + (struct-copy process p [state new-state]) + new-actions + w)]) + (lambda (exn) (kill-process pid exn w)))])) + +(define (update-process pid p actions w) + (let* ((w (struct-copy world w [process-table (hash-set (world-process-table w) pid p)])) + (w (mark-pid-runnable w pid))) + (enqueue-actions w pid actions))) (define (invoke-process pid thunk k-ok k-exn) (define-values (ok? result) @@ -91,34 +106,26 @@ (define (mark-pid-runnable w pid) (struct-copy world w [runnable-pids (set-add (world-runnable-pids w) pid)])) -(define (apply-transition pid t w) - (match t - [#f w] - [(transition new-state new-actions) - (let* ((w (transform-process pid w (lambda (p) (struct-copy process p [state new-state]))))) - (enqueue-actions (mark-pid-runnable w pid) pid new-actions))])) - -(define (transform-process pid w fp) - (define pt (world-process-table w)) - (match (hash-ref pt pid) - [#f w] - [p (struct-copy world w [process-table (hash-set pt pid (fp p))])])) - (define (enqueue-actions w label actions) (struct-copy world w [pending-action-queue (queue-append-list (world-pending-action-queue w) (for/list [(a actions)] (cons label a)))])) -(define (spawn-world . boot-actions) +(define-syntax-rule (spawn-world boot-action ...) + (make-spawn-world (lambda () (list boot-action ...)))) + +(define (make-world boot-actions) + (world 0 + (list->queue (for/list ((a (in-list boot-actions))) (cons 'meta a))) + (set) + (matcher-empty) + (hash) + (matcher-empty))) + +(define (make-spawn-world boot-actions-thunk) (spawn world-handle-event - (lambda () (transition (world 0 - (make-queue) - (set) - (matcher-empty) - (hash) - (matcher-empty)) - '())))) + (lambda () (transition (make-world (boot-actions-thunk)) '())))) (define (transition-bind k t0) (match-define (transition state0 actions0) t0) @@ -156,39 +163,87 @@ (define ((perform-action label a) w) (match a [(spawn behavior boot) - (define new-pid (world-next-pid w)) - (invoke-process new-pid + (invoke-process 'booting boot - (lambda (initial-t) - (match-define (transition initial-state initial-actions) initial-t) - (define new-p (process (matcher-empty) behavior initial-state)) - (define new-w - (struct-copy world w - [next-pid (+ new-pid 1)] - [process-table - (hash-set (world-process-table w) new-pid new-p)])) - (mark-pid-runnable (enqueue-actions new-w new-pid initial-actions) - new-pid)) - (lambda (exn) (kill-process new-pid exn w)))] + (match-lambda + [(transition initial-state initial-actions) + (define new-p (process (matcher-empty) behavior initial-state)) + (define new-pid (world-next-pid w)) + (update-process new-pid + new-p + initial-actions + (struct-copy world w [next-pid (+ new-pid 1)]))]) + (lambda (exn) (kill-process 'booting exn w)))] [(quit) (kill-process label #f w)] [(? patch? delta-orig) (define p (hash-ref (world-process-table w) label)) - (define old-interests (cond - [p (process-interests p)] - [(meta-label? label) (world-environment-interests w)] - [else (matcher-empty)])) - (define old-routing-table (world-routing-table w)) - (define delta (limit-patch (label-patch delta-orig label) old-interests)) - (define delta-aggregate (compute-patch-aggregate delta label old-routing-table)) - (define new-routing-table (apply-patch label old-routing-table delta)) - (define affected-pids (compute-affected-pids ... + (if (not (or p (meta-label? label))) + (transition w '()) ;; ignore actions for nonexistent processes + (let () + (define old-interests (if (meta-label? label) + (world-environment-interests w) + (process-interests p))) + (define delta (limit-patch (label-patch delta-orig label) old-interests)) + (define new-interests (apply-patch old-interests delta)) + + (define old-routing-table (world-routing-table w)) + (define new-routing-table (apply-patch old-routing-table delta)) + (define delta-aggregate (compute-aggregate-patch delta label old-routing-table)) + + (define new-w + (if (meta-label? label) + (struct-copy world w + [routing-table new-routing-table] + [environment-interests new-interests]) + (let ((new-p (struct-copy process p [interests new-interests]))) + (struct-copy world w + [routing-table new-routing-table] + [process-table + (hash-set (world-process-table w) label new-p)])))) + + (define affected-pids + (let ((pids (compute-affected-pids old-routing-table delta))) + (if (meta-label? label) pids (set-add pids label)))) + + (transition (for/fold [(w new-w)] [(pid affected-pids)] + (if (equal? pid label) + (let* ((feedback (patch (biased-intersection new-routing-table + (patch-added delta)) + (biased-intersection old-routing-table + (patch-removed delta))))) + (send-event feedback label w)) + (let* ((p (hash-ref (world-process-table w) pid)) + (event (view-patch delta-aggregate (process-interests p)))) + (send-event event pid w)))) + (and (meta-label? label) + (drop-patch delta-aggregate)))))])) + +(define (compute-affected-pids routing-table delta) + (define cover (matcher-union (patch-added delta) (patch-removed delta))) + (matcher-match-matcher cover routing-table + #:seed (set) + #:combiner (lambda (v1 v2 acc) (set-union v2 acc)))) (define (step-children w) (define runnable-pids (world-runnable-pids w)) (if (set-empty? runnable-pids) #f ;; world is inert. - (transition (for/fold ([w (struct-copy world w [runnable-pids (set)])]) - [(pid (in-set runnable-pids))] - (define p (hash-ref (world-process-table w) pid (lambda () #f))) - (if (not p) w (deliver-event #f pid p w))) + (transition (for/fold [(w (struct-copy world w [runnable-pids (set)]))] + [(pid (in-set runnable-pids))] + (send-event #f pid w)) '()))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(module+ test + (require racket/pretty) + + (define (step* w) + (let loop ((w w) (actions '())) + (pretty-print w) + (match (world-handle-event #f w) + [#f (values w (flatten actions))] + [(transition new-w new-actions) (loop new-w (cons actions new-actions))]))) + + (step* (make-world '())) + ) diff --git a/prospect/patch.rkt b/prospect/patch.rkt index a3062fa..28d0e73 100644 --- a/prospect/patch.rkt +++ b/prospect/patch.rkt @@ -6,16 +6,21 @@ (struct-out at-meta) lift-patch drop-patch + strip-interests + label-interests + label-patch limit-patch + compute-aggregate-patch apply-patch compute-patch + biased-intersection + view-patch pretty-print-patch) (require racket/set) (require racket/match) (require "route.rkt") - (module+ test (require rackunit)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -29,13 +34,14 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(define at-meta-proj (compile-projection (at-meta (?!)))) +(define observe-proj (compile-projection (observe (?!)))) + (define (lift-patch p) (match-define (patch in out) p) (patch (pattern->matcher #t (at-meta (embedded-matcher in))) (pattern->matcher #t (at-meta (embedded-matcher out))))) -(define at-meta-proj (compile-projection (at-meta (?!)))) - (define (drop-interests pi) (matcher-project pi at-meta-proj #:project-success (lambda (v) #t) @@ -61,6 +67,11 @@ (patch (matcher-subtract in bound #:combiner (lambda (v1 v2) #f)) (matcher-intersect out bound #:combiner (lambda (v1 v2) v1)))) +(define (compute-aggregate-patch p label base) + (define (combiner v1 v2) (matcher-subtract-combiner v1 (set-remove v2 label))) + (patch (matcher-subtract (patch-added p) base #:combiner combiner) + (matcher-subtract (patch-removed p) base #:combiner combiner))) + (define (apply-patch base p) (match-define (patch in out) p) (matcher-union (matcher-subtract base out) in)) @@ -78,6 +89,18 @@ (patch (matcher-subtract new-base old-base) (matcher-subtract old-base new-base))) +(define (biased-intersection object subject) + (matcher-project (matcher-intersect (observe (embedded-matcher object)) + subject + #:combiner (lambda (v1 v2) #t)) + observe-proj + #:project-success (lambda (v) #t) + #:combiner (lambda (v1 v2) #t))) + +(define (view-patch p interests) + (patch (biased-intersection (patch-added p) interests) + (biased-intersection (patch-removed p) interests))) + (define (pretty-print-patch p) (match-define (patch in out) p) (printf "<<<<<<<< Removed:\n") @@ -122,6 +145,30 @@ (printf "\nlimit mc/mab ma:\n") (void (pretty-print-patch (limit-patch (patch mc mab) ma))) + (printf "\ncompute-aggregate-patch m*/m0 Q mab:\n") + (void (pretty-print-patch (compute-aggregate-patch (patch m* m0) 'Q mab))) + + (printf "\ncompute-aggregate-patch m0/m* Q mab:\n") + (void (pretty-print-patch (compute-aggregate-patch (patch m0 m*) 'Q mab))) + + (printf "\ncompute-aggregate-patch m*/m0 P mab:\n") + (void (pretty-print-patch (compute-aggregate-patch (patch m* m0) 'P mab))) + + (printf "\ncompute-aggregate-patch m0/m* P mab:\n") + (void (pretty-print-patch (compute-aggregate-patch (patch m0 m*) 'P mab))) + + (printf "\ncompute-aggregate-patch m*/m0 Q m*:\n") + (void (pretty-print-patch (compute-aggregate-patch (patch m* m0) 'Q m*))) + + (printf "\ncompute-aggregate-patch m0/m* Q m*:\n") + (void (pretty-print-patch (compute-aggregate-patch (patch m0 m*) 'Q m*))) + + (printf "\ncompute-aggregate-patch m*/m0 P m*:\n") + (void (pretty-print-patch (compute-aggregate-patch (patch m* m0) 'P m*))) + + (printf "\ncompute-aggregate-patch m0/m* P m*:\n") + (void (pretty-print-patch (compute-aggregate-patch (patch m0 m*) 'P m*))) + (printf "\nlift mc/mab:\n") (void (pretty-print-patch (lift-patch (patch mc mab)))) diff --git a/prospect/route.rkt b/prospect/route.rkt index 38339b0..208d8eb 100644 --- a/prospect/route.rkt +++ b/prospect/route.rkt @@ -24,6 +24,7 @@ pattern->matcher* matcher-union matcher-intersect + matcher-subtract-combiner matcher-subtract matcher-match-value matcher-match-matcher @@ -336,12 +337,14 @@ (lambda (h) #f) (lambda (h) #f))) +(define (matcher-subtract-combiner s1 s2) + (define r (set-subtract s1 s2)) + (if (set-empty? r) #f r)) + ;; Matcher Matcher -> Matcher ;; Removes re2's mappings from re1. ;; The combine-successes function should return #f to signal "no remaining success values". -(define (matcher-subtract re1 re2 #:combiner [combiner (lambda (s1 s2) - (define r (set-subtract s1 s2)) - (if (set-empty? r) #f r))]) +(define (matcher-subtract re1 re2 #:combiner [combiner matcher-subtract-combiner]) (matcher-recurse re1 re2 combiner