Compare commits

...

76 Commits
pr/43 ... main

Author SHA1 Message Date
Sam Caldwell fc6e012d1c fixups to get tests passing 2021-05-10 15:30:46 -04:00
Sam Caldwell 3b75881366 fix type of empty? 2021-05-06 10:10:25 -04:00
Sam Caldwell 690f9e65a8 more docs and cleanups 2021-05-05 12:52:07 -04:00
Sam Caldwell 4f6089c805 more docs and cleanup 2021-05-04 17:15:34 -04:00
Sam Caldwell aa74ffa14d remove outdated example 2021-05-04 09:49:21 -04:00
Sam Caldwell 09ce074125 work on typed syndicate docs 2021-05-03 14:59:14 -04:00
Sam Caldwell 3f6a5573e4 Allow importing structs without accessors and opaque external types 2021-04-27 16:51:28 -04:00
Sam Caldwell 98c58d3e6f Add a typed during/spawn and checks for overly broad interests 2021-04-22 15:38:15 -04:00
Sam Caldwell c3559f1611 Hide legacy typed/syndicate #lang, consolidate to the "roles" version 2021-04-22 12:09:57 -04:00
Sam Caldwell 8b67d0ba03 test on composing communication types 2021-04-22 11:45:09 -04:00
Sam Caldwell 52e64d6792 move spin scripts 2021-04-22 11:41:31 -04:00
Sam Caldwell 59183b5fe9 reorganize typed syndicate project structure 2021-04-21 10:39:30 -04:00
Sam Caldwell 0191461137 allow importing structs with unknown super-type 2021-04-15 10:54:10 -04:00
Sam Caldwell 6b46be34f9 first draft of verifying messages in spin backend 2021-03-04 11:08:06 -05:00
Sam Caldwell ff1ac58a36 fix issues with determining stop effects 2021-02-22 11:30:43 -05:00
Sam Caldwell c54b088a4d dramatically improve handling of cycles in compile/internal-events 2021-01-28 11:26:11 -05:00
Sam Caldwell d5894e400b prototype using syndicate msd logging for displaying spin counterexamples 2021-01-25 11:14:43 -05:00
Sam Caldwell b023753091 provide an interface for msd tracing 2021-01-25 11:13:12 -05:00
Sam Caldwell 04530893f4 some handling of cycles in spin traces 2021-01-22 10:38:10 -05:00
Sam Caldwell bd267cfaa9 Translate trail file counterexample back to a syndicate-level trace 2021-01-15 11:15:44 -05:00
Sam Caldwell d79378b4a3 clean up generated files 2021-01-11 12:10:05 -05:00
Sam Caldwell 7a8628880a LTL syntax plus form for model checking in typed syndicate 2021-01-11 11:52:00 -05:00
Sam Caldwell 145bc84e33 shell script for running spin 2021-01-11 11:50:50 -05:00
Sam Caldwell 549590d304 missed one 2021-01-11 11:50:05 -05:00
Sam Caldwell cb3f0546c0 notes in Makefile 2021-01-11 11:49:33 -05:00
Sam Caldwell 4e43c489d8 remove unused argument 2021-01-06 12:08:13 -05:00
Sam Caldwell d0f00779cd invoke spin from racket 2021-01-06 11:19:42 -05:00
Sam Caldwell 5a5c651321 Improve simulation checking/failure trace generation
Account for the case where the spec takes a step but the implementation
remains in the same state
2020-12-21 11:07:29 -05:00
Sam Caldwell 1fba368987 Caputre actor actions while booting up a ground dataspace
fixes an issue where a function that evaluates multiple `spawn` forms
only spawns the last actor
2020-12-14 14:22:32 -05:00
Sam Caldwell 7475c1896f stop tracking debugging file 2020-12-14 11:53:52 -05:00
Sam Caldwell 5a90933e9f More work on unit test style simulation checking 2020-12-14 11:50:24 -05:00
Sam Caldwell 8dda1ba6bf Manually assign Type kind to types instead of doing a full
serialize/deserialize

seems to make things as much as 5x faster, and half the code size
2020-12-11 16:40:03 -05:00
Sam Caldwell 45f140d642 add form for writing type to file 2020-12-11 16:40:03 -05:00
Sam Caldwell 95699308dd fix small issue 2020-12-11 16:40:03 -05:00
Sam Caldwell 362e102524 fix constructor resugaring to use the name with the right scopes 2020-12-11 16:40:03 -05:00
Sam Caldwell 78fee55ffa raise an error when pattern elaboration fails to find a real type 2020-12-11 16:40:02 -05:00
Sam Caldwell 2fd3771609 simplify hash impl a little 2020-12-11 16:40:02 -05:00
Sam Caldwell 8be62ed72c work on finding trace counterexample when finding subgraph 2020-12-11 16:40:02 -05:00
Sam Caldwell c9c2d2747b improve some error reporting by moving cuts 2020-12-11 16:40:02 -05:00
Sam Caldwell c20d075d03 fixups to tests 2020-12-11 16:40:02 -05:00
Sam Caldwell 6dd369b08f improvements on verification, nb AnyActor performance hell 2020-12-11 16:40:02 -05:00
Sam Caldwell c9a5af0d10 create lambda shortcut 2020-12-11 16:40:02 -05:00
Sam Caldwell 7d8b62ff02 first draft on finding simulation counterexamples 2020-12-11 16:40:02 -05:00
Sam Caldwell db2a8e1cec fix issues with require-struct accessors 2020-12-11 16:40:02 -05:00
Sam Caldwell 3e13e3e449 work on proto tie-in 2020-12-11 16:40:00 -05:00
Sam Caldwell 8a6931710a create a typed struct out 2020-12-11 16:40:00 -05:00
Sam Caldwell 1805b936be try syntax-local-lift-module-end for lift+define-role 2020-12-11 16:40:00 -05:00
Sam Caldwell 25860019c6 define accessors for require-struct 2020-12-11 16:40:00 -05:00
Sam Caldwell abecc4996c first bit of linking proto analysis into language 2020-12-11 16:40:00 -05:00
Sam Caldwell d523dc7937 define constructor accessors 2020-12-11 16:40:00 -05:00
Sam Caldwell e75af5ae1c infer a type for fields sans declared type 2020-12-11 16:40:00 -05:00
Sam Caldwell 4cd90a6295 add more require & provide specs 2020-12-11 16:40:00 -05:00
Sam Caldwell f040a6db7e create typed timestate driver wrapper 2020-12-11 16:40:00 -05:00
Sam Caldwell e5b797b450 fix the type of run-ground-dataspace 2020-12-11 16:39:59 -05:00
Sam Caldwell bdf4c30218 add multi-accumulator for/fold 2020-12-11 16:39:59 -05:00
Sam Caldwell 04b58f9d9f add string=? 2020-12-11 16:39:59 -05:00
Sam Caldwell b66ab0bfcd add some list ops 2020-12-11 16:39:59 -05:00
Sam Caldwell 733c874871 add argmin and argmax 2020-12-11 16:39:59 -05:00
Sam Caldwell fe6435f056 add in-hash-keys and in-hash-values 2020-12-11 16:39:59 -05:00
Sam Caldwell 659715cd0e fix require, add current-inexact-milliseconds primop 2020-12-11 16:39:59 -05:00
Sam Caldwell 8446a0d770 customize resugaring, clean up a bit 2020-12-11 16:39:59 -05:00
Sam Caldwell 8288312890 remove debug prints 2020-12-11 16:39:59 -05:00
Sam Caldwell 967da40b80 lift syntax-parse out of templates 2020-12-11 16:39:59 -05:00
Sam Caldwell 1e434f8006 print less 2020-12-11 16:39:58 -05:00
Michael Ballantyne c988c4f462 preserve sharing in serializer 2020-12-11 16:39:58 -05:00
Sam Caldwell db3fc2acd9 uncomment flink 2020-12-11 16:39:58 -05:00
Michael Ballantyne 50d2d1a6fa fix the serializer 2020-12-11 16:39:58 -05:00
Sam Caldwell 122ef0b5f9 try out the syntax serializer 2020-12-11 16:39:58 -05:00
Sam Caldwell e1ca7ba2c4 debug state 2020-12-11 16:39:58 -05:00
Sam Caldwell 27b83e5e0a Fix issue keep debugging 2020-12-11 16:39:58 -05:00
Sam Caldwell a1660114df work towards using typedefs, debugging 2020-12-11 16:39:58 -05:00
Sam Caldwell 074ec24da4 workaround: combine big and little lambda 2020-12-11 16:39:57 -05:00
Sam Caldwell 48344856c3 wip on typedefs 2020-12-11 16:39:57 -05:00
Sam Caldwell 165dfeb6c8 fix bug I introduced 2020-12-11 16:38:56 -05:00
Sam Caldwell 38b5e34efb check context of on-start and on-stop as well 2020-12-10 15:08:19 -05:00
Sam Caldwell e2bb438704 Perform error/checking and reporting for non-spawn actions at the module
top level and endpoint installation out of context
2020-12-10 13:00:08 -05:00
94 changed files with 4065 additions and 1613 deletions

View File

@ -409,6 +409,7 @@
[(_ [id:id init maybe-contract ...] ...)
(quasisyntax/loc stx
(begin
(ensure-in-endpoint-context! 'field)
(when (and (in-script?) (pair? (current-facet-id)))
(error 'field
"~a: Cannot declare fields in a script; are you missing a (react ...)?"
@ -487,13 +488,17 @@
(syntax-parse stx
[(_ script ...)
(quasisyntax/loc stx
(schedule-script! (lambda () (begin/void-default script ...))))]))
(begin
(ensure-in-endpoint-context! 'on-start)
(schedule-script! (lambda () (begin/void-default script ...)))))]))
(define-syntax (on-stop stx)
(syntax-parse stx
[(_ script ...)
(quasisyntax/loc stx
(add-stop-script! (lambda () (begin/void-default script ...))))]))
(begin
(ensure-in-endpoint-context! 'on-stop)
(add-stop-script! (lambda () (begin/void-default script ...)))))]))
(define-syntax (on-event stx)
(syntax-parse stx
@ -1116,11 +1121,12 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Endpoint Creation
(define (ensure-in-endpoint-context! who)
(when (or (in-script?) (null? (current-facet-id)))
(error who "Attempt to add endpoint out of installation context; are you missing a (react ...)?")))
(define (add-endpoint! where internal? patch-fn handler-fn)
(when (in-script?)
(error 'add-endpoint!
"~a: Cannot add endpoint in script; are you missing a (react ...)?"
where))
(ensure-in-endpoint-context! 'add-endpoint!)
(define-values (new-eid delta-aggregate)
(let ()
(define a (current-actor-state))

View File

@ -8,6 +8,7 @@
(require "main.rkt")
(require (submod "actor.rkt" for-module-begin))
(require "store.rkt")
(require (only-in "core.rkt" clean-actions))
(provide (rename-out [module-begin #%module-begin])
activate
@ -71,6 +72,13 @@
#%declare
begin-for-declarations))))
(define (ensure-spawn-actions! acts)
(define cleaned-acts (clean-actions acts))
(for ([act (in-list cleaned-acts)]
#:unless (actor? act))
(raise-argument-error 'syndicate-module "top-level actor creation action" act))
cleaned-acts)
(define-syntax (syndicate-module stx)
(syntax-parse stx
[(_ (action-ids ...) (form forms ...))
@ -89,8 +97,9 @@
#`(begin
(define-values (tmp ...) (values #,@(make-list (length (syntax->list #'(x ...))) #'#f)))
(define action-id
(capture-actor-actions
(lambda () (set!-values (tmp ...) e))))
(ensure-spawn-actions!
(capture-actor-actions
(lambda () (set!-values (tmp ...) e)))))
(define-values (x ...) (values tmp ...))
(syndicate-module (action-ids ... action-id) (forms ...)))]
[(head rest ...)
@ -99,8 +108,9 @@
#`(begin #,expanded (syndicate-module (action-ids ...) (forms ...)))]
[else
(with-syntax ([action-id (car (generate-temporaries (list #'form)))])
#`(begin (define action-id (capture-actor-actions (lambda () #,expanded)))
(syndicate-module (action-ids ... action-id) (forms ...))))])]
#`(begin
(define action-id (ensure-spawn-actions! (capture-actor-actions (lambda () #,expanded))))
(syndicate-module (action-ids ... action-id) (forms ...))))])]
[non-pair-syntax
#'(begin form (syndicate-module (action-ids ...) (forms ...)))])]
[(_ (action-ids ...) ())

View File

@ -60,7 +60,10 @@
(quit))]
[_ #f]))
(message (set-timer 'tick 1000 'relative))
(actor (lambda (e s) (quit))
#f
(message (set-timer 'tick 1000 'relative)))
(actor ticker
1
(patch-seq (sub (observe (set-timer ? ? ?)))

View File

@ -13,6 +13,7 @@
(require (for-syntax syntax/parse))
(require rackunit)
(require racket/engine)
(require racket/exn)
(define mt-scn (scn trie-empty))
@ -289,7 +290,7 @@
;; leaf behavior function
(define (actor-behavior e s)
(when e
(with-handlers ([exn:fail? (lambda (e) (eprintf "exception: ~v\n" e) (quit #:exception e (list)))])
(with-handlers ([exn:fail? (lambda (e) (printf "exception: ~v\n" (exn->string e)) (quit #:exception e (list)))])
(match-define (actor-state π-old fts) s)
(define-values (actions next-fts)
(for/fold ([as '()]
@ -545,7 +546,7 @@
;; boot-actor : actor Γ -> Action
(define (boot-actor a Γ)
(with-handlers ([exn:fail? (lambda (e)
(eprintf "booting actor died with: ~v\n" e)
(printf "booting actor died with: ~a\n" (exn->string e))
#f)])
(match a
[`(spawn ,O ...)

View File

@ -1,5 +1,7 @@
#lang racket/base
(provide start-tracing!)
(require racket/match)
(require racket/set)
(require racket/string)
@ -12,7 +14,7 @@
(define-logger syndicate/trace/msd)
(let ((output-filename (getenv "SYNDICATE_MSD")))
(define (start-tracing! output-filename)
(when output-filename
(define names (make-hash (list (cons '() "'ground"))))
(define (open-output cause)
@ -104,3 +106,5 @@
(loop)))))
(channel-get ch)
(current-trace-procedures (cons msd-trace (current-trace-procedures))))))
(start-tracing! (getenv "SYNDICATE_MSD"))

View File

@ -6,5 +6,6 @@ pan.c : leader-and-seller.pml
# -a to analyze, -f for (weak) fairness
# -n to elide report of unreached states
# -N spec-name to verify a particular specification
check: pan
./pan -a -f
./pan -a -f -n

View File

@ -1,635 +0,0 @@
#lang racket
;; TODO - syntax for LTL
(require "proto.rkt")
(require "ltl.rkt")
(module+ test
(require rackunit)
(require "test-utils.rkt"))
;; a SpinProgram is a
;; (sprog Assignment [Listof SpinProcess] [LTL SName])
(struct sprog [assignment procs spec] #:transparent)
;; a SpinProcess is a
;; (sproc SName [Setof SName] Assignment [Listof SName] [Setof SpinState])
(struct sproc [name state-names init initial-assertions states] #:transparent)
;; an Assignment is a [Hashof SVar SValue]
;; a SName is a Symbol that is a legal variable name in Spin
;; a SVar is a
;; (svar SName SType)
(struct svar [name ty] #:transparent)
;; a SValue is one of
;; - Int
;; - Bool
;; - SName
;; and must be a valid Spin literal
;; a SType is one of
;; - 'SInt
;; - 'SBool
;; - 'mtype
(define SInt 'SInt)
(define SBool 'SBool)
(define mtype 'mtype)
;; a SpinState is a
;; (sstate SName [Sequenceof SBranch])
(struct sstate [name branches] #:transparent)
;; a SBranch is a
;; (sbranch D+ SName [Listof SAction])
(struct sbranch [event dest actions] #:transparent)
;; a SAction is one of
;; - (assert ?)
;; - (retract ?)
;; - (send ?)
;; - (incorporate D+)
;; - (transition-to SName)
(struct assert [ty] #:transparent)
(struct retract [ty] #:transparent)
;; send defined in proto.rkt
(struct incorporate [evt] #:transparent)
(struct transition-to [dest] #:transparent)
;; each process has a local variable that determines its current state
(define CURRENT-STATE (svar 'current mtype))
;; a NameEnvironment is a [Hashof τ SName]
;; [Sequenceof RoleGraph] [LTL τ] -> SpinProgram
(define (program->spin rgs [spec #t])
(define assertion-tys (all-assertions rgs))
(define event-tys (all-events rgs))
(define event-subty# (make-event-map assertion-tys event-tys))
(define all-mentioned-tys (set-union assertion-tys event-tys))
(define name-env (make-name-env all-mentioned-tys))
(define globals (initial-assertion-vars-for all-mentioned-tys name-env))
(define procs (for/list ([rg rgs]) (rg->spin rg event-subty# name-env)))
(define spec-spin (rename-ltl spec name-env))
(sprog globals procs spec-spin))
;; RoleGraph [Hashof τ [Setof τ]] NameEnvironment -> SpinProcess
(define (rg->spin rg event-subty# name-env #:name [name (gensym 'proc)])
(match-define (role-graph st0 states) rg)
(define all-events (all-event-types (in-hash-values states)))
(define state-renaming (make-state-rename (hash-keys states)))
(define states- (for/list ([st (in-hash-values states)])
(state->spin st states event-subty# name-env state-renaming)))
(define st0- (hash-ref state-renaming st0))
;; ergh the invariant for when I tack on _assertions to a name is getting tricksy
(define st0-assertions (rename-all name-env (super-type-closure (state-assertions (hash-ref states st0)) event-subty#)))
(define assignment (local-variables-for st0- all-events name-env))
(sproc name (hash-values-set state-renaming) assignment st0-assertions (list->set states-)))
;; State [Sequenceof State] [Hashof τ [Setof τ]] NameEnvironment [Hashof StateName SName] -> SpinState
(define (state->spin st states event-subty# name-env state-env)
(match-define (state name transitions assertions) st)
(define name- (hash-ref state-env name))
(define branches (for*/list ([(D+ txns) (in-hash transitions)]
[txn (in-set txns)])
(match-define (transition effs dest) txn)
(match-define (state _ _ dest-assertions) (hash-ref states dest))
(define dest- (hash-ref state-env dest))
(branch-on D+ assertions dest- dest-assertions effs event-subty# name-env)))
(sstate name- branches))
;; [Setof τ] -> NameEnvironment
(define (make-name-env tys)
(let loop ([name-depth 3])
(when (> name-depth 10)
(raise-argument-error 'make-name-env "types able to be named" tys))
(define renaming
(for/hash ([ty (in-set tys)])
(values ty
(type->id ty #:depth name-depth))))
(define names (hash-values-set renaming))
(cond
[(equal? (set-count names) (set-count tys))
renaming]
[else
(loop (add1 name-depth))])))
;; SName -> SName
(define (assertions-var-name s)
(string->symbol (format "~a_assertions" s)))
;; SName -> SName
(define (active-var-name s)
(string->symbol (format "know_~a" s)))
;; [Setof τ] [Setof τ] -> [Hashof τ [Setof τ]]
(define (make-event-map assertion-tys event-tys)
;; TODO - potentially use non-empty intersection
(for/hash ([a (in-set assertion-tys)])
(values a
(all-supertypes-of a event-tys))))
;; τ [Setof τ] -> [Setof τ]
(define (all-supertypes-of τ tys)
(for*/set ([ty (in-set tys)]
#:when (<:? τ ty))
ty))
;; [Setof τ] [Hashof τ [Setof τ]]
(define (super-type-closure asserts event-subty#)
(for*/set ([a (in-set asserts)]
[supers (in-value (hash-ref event-subty# a))]
[τ (in-set (set-add supers a))])
τ))
;; [Setof τ] NameEnvironment -> Assignment
(define (initial-assertion-vars-for assertion-tys name-env)
(for/hash ([τ (in-set assertion-tys)])
(values (svar (assertions-var-name (hash-ref name-env τ)) SInt)
0)))
;; NameEnvironment [Setof τ] -> [Sequenceof SName]
(define (rename-all name-env asserts)
(for/set ([a (in-set asserts)])
(hash-ref name-env a)))
;; [Sequenceof RoleGraph] -> [Setof τ]
(define (all-assertions rgs)
;; RoleGraph -> (Setof τ)
(define (all-assertions-of rg)
(for*/set ([st (in-hash-values (role-graph-states rg))]
[τ (in-set (state-assertions st))])
τ))
(for/fold ([as (set)])
([rg rgs])
(set-union as (all-assertions-of rg))))
;; [Sequenceof RoleGraph] -> [Setof τ]
(define (all-events rgs)
;; RoleGraph -> (Setof τ)
(define (all-events-of rg)
(all-event-types (hash-values (role-graph-states rg))))
(for/fold ([as (set)])
([rg rgs])
(set-union as (all-events-of rg)))
)
;; [Sequenceof State] -> ?
(define (all-event-types states)
(for*/set ([st states]
[D+ (in-hash-keys (state-transitions st))])
(match D+
[(or (Asserted τ) (Retracted τ))
τ]
[(Message τ)
(raise-argument-error 'all-event-types "messages not supported yet" D+)]
[_
(raise-argument-error 'all-event-types "internal events not allowed" D+)])))
;; SName [Setof τ] NameEnvironment -> Assignment
(define (local-variables-for st0 all-events name-env)
(define assign
(for/hash ([evt (in-set all-events)])
(values (svar (active-var-name (hash-ref name-env evt))
SBool)
#f)))
(hash-set assign CURRENT-STATE st0))
;; D+ [Setof τ] SName [Setof τ] [Listof TransitionEffect] [Hashof τ [Setof τ]] NameEnvironment -> SBranch
(define (branch-on D+ curr-assertions dest dest-assertions effs event-subty# name-env)
(define new-assertions (super-type-closure (set-subtract dest-assertions curr-assertions) event-subty#))
(define retractions (super-type-closure (set-subtract curr-assertions dest-assertions) event-subty#))
(define (lookup ty) (hash-ref name-env ty))
(define asserts (set-map new-assertions (compose assert lookup)))
(define retracts (set-map retractions (compose retract lookup)))
(unless (empty? effs)
(raise-argument-error 'branch-on "messages not supported" effs))
(define renamed-evt (rename-event D+ name-env))
(sbranch renamed-evt dest (list* (transition-to dest)
(incorporate renamed-evt)
(append asserts retracts effs))))
;; D+ NameEnvironment -> D+
(define (rename-event D+ name-env)
(match D+
[(Asserted τ)
(Asserted (hash-ref name-env τ))]
[(Retracted τ)
(Retracted (hash-ref name-env τ))]
[(Message τ)
(raise-argument-error 'rename-event "messages not implemented yet" D+)]))
;; [LTL τ] -> [LTL SName]
(define (rename-ltl ltl name-env)
(define (lookup τ) (hash-ref name-env τ))
(map-atomic ltl lookup))
(module+ test
(test-case
"sanity: compile book seller type"
(define/timeout seller-rg (compile seller-actual))
(define name-env (hash
(Observe (Observe (Struct 'BookQuoteT (list (Base 'String) (Mk⋆)))))
'Obs_Obs_BookQuoteT
(Observe (Struct 'BookQuoteT (list (Base 'String) (Mk⋆))))
'Obs_BookQuoteT_String_star
(Struct 'BookQuoteT (list (Base 'String) (U (list (Base 'Int) (Base 'Int)))))
'BookQuoteT_String_U_Int_Int))
(define event# (hash
(Observe (Observe (Struct 'BookQuoteT (list (Base 'String) (Mk⋆)))))
(set)
(Struct 'BookQuoteT (list (Base 'String) (U (list (Base 'Int) (Base 'Int)))))
(set)))
(define/timeout seller-spin (rg->spin seller-rg event# name-env))
(check-true (sproc? seller-spin))))
(define tab-level (make-parameter 0))
(define TAB-WIDTH 2)
(define (indent)
(display (make-string (* TAB-WIDTH (tab-level)) #\space)))
(define-syntax-rule (with-indent bdy ...)
(parameterize ([tab-level (add1 (tab-level))])
bdy ...))
(define SPIN_ID_RX #rx"[a-zA-Z][a-zA-Z0-9_]*")
(define SPIN_ID_TRAILING_CHAR #rx"[a-zA-Z0-9_]+")
;; (U Symbol String) -> Bool
(define (spin-id? s)
(when (symbol? s)
(set! s (symbol->string s)))
(regexp-match? SPIN_ID_RX s))
(define SPIN-KEYWORDS
'(active assert atomic bit bool break byte chan d_step D_proctype do
else empty enabled fi full goto hidden if init int len mtype nempty
never nfull od of pc_value printf priority proctype provided run
short skip timeout typedef unless unsigned xr xs))
;; Symbol -> Symbol
(define (unkeyword s)
(if (member s SPIN-KEYWORDS)
(gensym s)
s))
;; (U Symbol String) -> SName
(define (make-spin-id s)
(when (symbol? s)
(set! s (symbol->string s)))
(define with_legal_prefix (string-append "ty_" s))
(match (regexp-match* SPIN_ID_TRAILING_CHAR with_legal_prefix)
['("ty_")
(raise-argument-error 'make-spin-id "unable to make spin id" s)]
[(cons fst rst)
(define match-str (apply string-append fst rst))
(define without-added-prefix (substring match-str 3))
(if (spin-id? without-added-prefix)
(unkeyword (string->symbol without-added-prefix))
(unkeyword (string->symbol match-str)))]))
;; τ -> SName
(define (type->id ty #:depth [depth 3])
(define ctors (type-constructors ty depth))
(define rough-name (string-join (map symbol->string ctors) "_"))
(make-spin-id rough-name))
;; [Listof StateName] -> [Hashof StateName SName]
(define (make-state-rename state-names)
(let loop ([prefix 3])
(define renaming (for/hash ([nm (in-list state-names)])
(values nm
(state-name->spin-id nm #:prefix prefix))))
(define distinct-names (hash-values-set renaming))
(cond
[(equal? (set-count distinct-names) (set-count state-names))
renaming]
[(> prefix 20)
(raise-argument-error 'make-state-rename "able to make renaming" state-names)]
[else
(loop (add1 prefix))])))
;; StateName -> SName
(define (state-name->spin-id nm #:prefix [prefix 3])
(cond
[(set-empty? nm)
(gensym 'inert)]
[else
(define (take-prefix s) (substring s 0 (min prefix (string-length s))))
(define rough-name (string-join (set-map nm (compose take-prefix symbol->string)) "_"))
(make-spin-id rough-name)]))
;; τ -> [Listof Symbol]
(define (type-constructors ty depth)
(cond
[(zero? depth) '()]
[else
(match ty
[(Struct name tys)
;; TODO - consider camel-casing struct name
(cons name (append-map (λ (ty) (type-constructors ty (sub1 depth))) tys))]
[(Observe ty)
(cons 'Obs (type-constructors ty (sub1 depth)))]
[(U tys)
(cons 'U (append-map (λ (ty) (type-constructors ty (sub1 depth))) tys))]
[(== )
(list 'star)]
[(Base name)
(list name)]
[(List _)
(list 'List)]
[(Set _)
(list 'Set)]
[(Hash _ _)
(list 'Hash)]
[(internal-label _ _)
(raise-argument-error 'type-constructors "internal events not supported" ty)])]))
(module+ test
(test-case
"type-constructors basics"
(define bi (Struct 'BookInterestT (list (Base 'String) (Base 'String) (Base 'Bool))))
(check-equal? (type-constructors bi 1)
'(BookInterestT))
(check-equal? (type-constructors bi 2)
'(BookInterestT String String Bool))
(check-equal? (type-constructors bi 3)
'(BookInterestT String String Bool))
(check-equal? (type-constructors (Observe bi) 3)
'(Obs BookInterestT String String Bool)))
(test-case
"type->id basics"
(define bi (Struct 'BookInterestT (list (Base 'String) (Base 'String) (Base 'Bool))))
(check-equal? (type->id bi)
'BookInterestT_String_String_Bool)
(check-equal? (type->id (Observe bi))
'Obs_BookInterestT_String_String_Bool)
(check-equal? (type->id (Struct 'hi-mom '()))
'himom)
(check-equal? (type->id bi #:depth 1)
'BookInterestT)
(check-exn exn:fail?
(lambda () (type->id (Struct '--- '())))
"unable to make spin id")))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Code Generation
(define SPIN-PRELUDE (file->string "spin-prelude.pml"))
;; SpinThang FilePath -> Void
(define (gen-spin/to-file spin name)
(with-output-to-file name
(lambda () (gen-spin spin))
#:mode 'text
#:exists 'replace))
;; SpinThang -> Void
(define (gen-spin spin)
(match spin
[(sprog assignment procs spec)
(display SPIN-PRELUDE)
(gen-assignment assignment)
(newline)
(for ([p procs])
(gen-spin p)
(newline))
(gen-spec "spec" (lambda () (gen-ltl spec)))
(newline)
(gen-sanity-ltl assignment)]
[(sproc name state-names init asserts states)
(indent) (declare-mtype state-names)
(indent) (printf "active proctype ~a() {\n" name)
(with-indent
(gen-assignment init)
(for ([a asserts])
(gen-spin (assert a)))
(indent) (displayln "end: do")
(with-indent
(for ([st states])
(gen-spin st)))
(indent) (displayln "od;")
)
(indent) (displayln "}")]
[(sstate name branches)
(indent) (printf ":: ~a == ~a ->\n" (svar-name CURRENT-STATE) name)
(with-indent
(indent) (displayln "if")
(with-indent
(cond
[(empty? branches)
(indent) (displayln ":: true -> skip;")]
[else
(for ([branch branches])
(gen-spin branch))]))
(indent) (displayln "fi;"))]
[(sbranch event dest actions)
(indent) (printf ":: ~a ->\n" (predicate-for event))
;; TODO - make the body atomic
(with-indent
(indent) (displayln "atomic {")
(with-indent
(for ([act actions])
(gen-spin act)))
(indent) (displayln "}"))]
[(assert x)
(indent) (printf "ASSERT(~a);\n" x)]
[(retract x)
(indent) (printf "RETRACT(~a);\n" x)]
[(send x)
(raise-argument-error 'gen-spin "message sending not supported yet" spin)]
[(incorporate evt)
(indent) (update-for evt)]
[(transition-to dest)
(indent) (printf "~a = ~a;\n" (svar-name CURRENT-STATE) dest)]))
;; [Setof SName] -> Void
(define (declare-mtype state-names)
(display "mtype = {")
(display (string-join (set-map state-names symbol->string) ", "))
(displayln "}"))
;; Assignment -> Void
(define (gen-assignment assign)
(for ([(var val) (in-hash assign)])
(indent) (printf "~a = ~a;\n"
(declare-var var)
(spin-val->string val))))
;; SVar -> Void
(define (declare-var var)
(match-define (svar name ty) var)
(format "~a ~a" (spin-type->string ty) name))
;; SValue -> String
(define (spin-val->string v)
(cond
[(boolean? v)
(if v "true" "false")]
[(exact-integer? v)
(~a v)]
[(symbol? v)
(~a v)]))
;; SType -> String
(define (spin-type->string ty)
(match ty
[(== SInt) "int"]
[(== SBool) "bool"]
[(== mtype) "mtype"]))
;; D+ -> String
(define (predicate-for event)
(match event
[(Asserted nm)
(define assertion-var nm)
(define active-var (active-var-name nm))
(format "ASSERTED(~a) && !~a" assertion-var active-var)]
[(Retracted nm)
(define assertion-var nm)
(define active-var (active-var-name nm))
(format "RETRACTED(~a) && ~a" assertion-var active-var)]
[(Message nm)
(raise-argument-error 'predicate-for "message sending not supported yet" event)]))
;; D+ -> Void
(define (update-for event)
(match event
[(Asserted nm)
(define active-var (active-var-name nm))
(printf "~a = ~a;\n" active-var (spin-val->string #t))]
[(Retracted nm)
(define active-var (active-var-name nm))
(printf "~a = ~a;\n" active-var (spin-val->string #f))]
[(Message nm)
(raise-argument-error 'predicate-for "message sending not supported yet" event)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; LTL
;; String {-> Void} -> Void
(define (gen-spec name mk-body)
(indent) (printf "ltl ~a {\n" name)
(with-indent
(mk-body))
(newline)
(indent) (displayln "}"))
;; [LTL SName] -> Void
(define (gen-ltl ltl)
(match ltl
[(always p)
(indent) (displayln "[](")
(with-indent
(gen-ltl p))
(indent) (displayln ")")]
[(eventually p)
(indent) (displayln "<>(")
(with-indent
(gen-ltl p))
(indent) (displayln ")")]
[(weak-until p q)
(gen-ltl-bin-op "W" p q)]
[(strong-until p q)
(gen-ltl-bin-op "U" p q)]
[(ltl-implies p q)
(gen-ltl-bin-op "->" p q)]
[(ltl-and p q)
(gen-ltl-bin-op "&&" p q)]
[(ltl-or p q)
(gen-ltl-bin-op "||" p q)]
[(ltl-not p)
(indent) (display "!(")
(gen-ltl p)
(displayln ")")]
[(atomic nm)
(printf "ASSERTED(~a)\n" nm)]
[#t
(display "true")]
[#f
(display "false")]))
;; String [LTL SName] [LTL SName] -> Void
(define (gen-ltl-bin-op name p q)
(indent) (display "(") (gen-ltl p) (display ") ")
(displayln name)
(newline)
(indent) (display "(") (gen-ltl q) (displayln ")"))
;; Assignment -> Void
(define (gen-sanity-ltl assignment)
(gen-spec "sanity"
(lambda ()
(indent) (displayln "[](")
(with-indent
(for ([assertion-var (in-hash-keys assignment)])
(indent) (printf "~a >= 0 &&\n" (svar-name assertion-var)))
(indent) (displayln "true"))
(indent) (displayln ")"))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Misc Utils
;; [Hashof K V] -> [Setof V]
(define (hash-values-set h)
(for/set ([x (in-hash-values h)])
x))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Test Case
(module+ leader-and-seller
(define leader-rg (compile (parse-T
'(Role ; = react
(get-quotes)
(Reacts ; = on
(Asserted (BookQuoteT String (Bind Int)))
(Branch
(Effs (Branch (Effs (Stop get-quotes)) (Effs)))
(Effs
(Role
(poll-members)
(Reacts
(Asserted (BookInterestT String (Bind String) Discard))
(Branch
(Effs (Stop poll-members (Branch (Effs (Stop get-quotes)) (Effs))))
(Effs))
(Branch
(Effs
(Stop get-quotes (Role (announce) (Shares (BookOfTheMonthT String)))))
(Effs)))
(Reacts (Retracted (BookInterestT String (Bind String) Bool)))
(Reacts (Asserted (BookInterestT String (Bind String) Bool)))
(Reacts (Retracted (BookInterestT String (Bind String) Bool)))
(Reacts (Asserted (BookInterestT String (Bind String) Bool)))))))
(Reacts (Retracted (ClubMemberT (Bind String))))
(Reacts (Asserted (ClubMemberT (Bind String))))))))
(define seller-rg (compile seller-actual))
(define member-rg (compile member-actual))
(define bq (book-quote String ))
(define bi (book-interest String ))
(define book-club-spec
(&& (eventually (atomic bq))
(always (ltl-implies (atomic (Observe bq))
(eventually (atomic bq))))
(always (ltl-implies (atomic (Observe bi))
(eventually (atomic bi))))))
(define book-club-spin (program->spin (list leader-rg seller-rg member-rg)
book-club-spec))
(gen-spin/to-file book-club-spin "gen-book-club.pml"))
(module+ flink
(define (import r)
(define r+ (parse-T r))
(compile/internal-events (compile r+) #f))
(define jm-rg (import job-manager-actual))
(define tm-rg (import task-manager-ty))
(define tr-rg (import task-runner-ty))
(define flink-spin (program->spin (list tr-rg tm-rg jm-rg)))
(gen-spin/to-file flink-spin "gen-flink.pml"))

View File

@ -1,27 +0,0 @@
#lang typed/syndicate
(define-type-alias ds-type
(U (Observe (Tuple String )) (Tuple String Int)))
(dataspace ds-type
(spawn ds-type
(facet _
(fields [the-thing Int 0])
(assert (tuple "the thing" (ref the-thing)))
(on (asserted (tuple "set thing" (bind new-v Int)))
(set! the-thing new-v))))
(spawn ds-type
(let [k (λ [10 (begin)]
[(bind x Int)
(facet _ (fields)
(assert (tuple "set thing" (+ x 1))))])]
(facet _ (fields)
(on (asserted (tuple "the thing" (bind x Int)))
(k x)))))
(spawn ds-type
(facet _ (fields)
(on (asserted (tuple "the thing" (bind t Int)))
(displayln t)))))

View File

@ -1,14 +1,19 @@
#lang typed/syndicate
;; Expected Output
;; 0
;; 70
;; #f
(define-constructor (account balance)
#:type-constructor AccountT
#:with Account (AccountT Int)
#:with AccountRequest (AccountT ))
#:with AccountRequest (AccountT /t))
(define-constructor (deposit amount)
#:type-constructor DepositT
#:with Deposit (DepositT Int)
#:with DepositRequest (DepositT ))
#:with DepositRequest (DepositT /t))
(define-type-alias ds-type
(U Account
@ -18,45 +23,43 @@
(Observe DepositRequest)
(Observe (Observe DepositRequest))))
(dataspace ds-type
(define-type-alias account-manager-role
(Role (account-manager)
(Shares Account)
(Reacts (Asserted Deposit))))
(spawn ds-type
(facet _
(fields [balance Int 0])
(assert (account (ref balance)))
(on (asserted (deposit (bind amount Int)))
(set! balance (+ (ref balance) amount)))))
(define-type-alias client-role
(Role (client)
(Reacts (Asserted Account))))
(spawn ds-type
(facet _
(fields)
(on (asserted (account (bind amount Int)))
(displayln amount))))
(spawn ds-type
(facet _
(fields)
(on (asserted (observe (deposit discard)))
(facet _
(fields)
(assert (deposit 100))
(assert (deposit -30)))))))
(run-ground-dataspace ds-type
#|
;; Hello-worldish "bank account" example.
(spawn ds-type
(lift+define-role acct-mngr-role
(start-facet account-manager
(field [balance Int 0])
(assert (account (ref balance)))
(on (asserted (deposit (bind amount Int)))
(set! balance (+ (ref balance) amount))))))
(struct account (balance) #:prefab)
(struct deposit (amount) #:prefab)
(spawn ds-type
(lift+define-role obs-role
(start-facet observer
(on (asserted (account (bind amount Int)))
(displayln amount)))))
(spawn (field [balance 0])
(assert (account (balance)))
(on (message (deposit $amount))
(balance (+ (balance) amount))))
(spawn ds-type
(lift+define-role buyer-role
(start-facet buyer
(on (asserted (observe (deposit discard)))
(start-facet deposits
(assert (deposit 100))
(assert (deposit -30))))))))
(spawn (on (asserted (account $balance))
(printf "Balance changed to ~a\n" balance)))
(spawn* (until (asserted (observe (deposit _))))
(send! (deposit +100))
(send! (deposit -30)))
|#
(module+ test
(check-simulates acct-mngr-role account-manager-role)
(check-simulates obs-role client-role)
;; Tried to write this, then it failed, I looked and buyer doesn't actually implement that spec
#;(check-simulates buyer-role client-role)
)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
;; Expected Output
;; leader learns that there are 5 copies of The Wind in the Willows
@ -59,17 +59,19 @@
(Role (_)
;; nb no mention of retracting this assertion
(Shares (BookQuoteT String Int))))))
(export-type "seller-role.rktd" seller-role)
(define (spawn-seller [inventory : Inventory])
(spawn τc
(begin
(export-roles "seller-impl.rktd"
(lift+define-role seller-impl
(start-facet seller
(field [books Inventory inventory])
;; Give quotes to interested parties.
(during (observe (book-quote $title _))
;; TODO - lookup
(assert (book-quote title (lookup title (ref books)))))))))
(assert (book-quote title (lookup title (ref books))))))))))
(define-type-alias leader-role
(Role (leader)
@ -77,16 +79,16 @@
(Role (poll)
(Reacts (Asserted (BookInterestT String String Bool))
;; this is actually implemented indirectly through dataflow
(U (Stop leader
(Role (_)
(Shares (BookOfTheMonthT String))))
(Stop poll)))))))
(Branch (Stop leader
(Role (_)
(Shares (BookOfTheMonthT String))))
(Stop poll)))))))
(define-type-alias leader-actual
(Role (get-quotes31)
(Role (get-quotes)
(Reacts (Asserted (BookQuoteT String (Bind Int)))
(Stop get-quotes)
(Role (poll-members36)
(Role (poll-members)
(Reacts OnDataflow
(Stop poll-members
(Stop get-quotes))
@ -102,7 +104,8 @@
(define (spawn-leader [titles : (List String)])
(spawn τc
(print-role
(export-roles "leader-impl.rktd"
(lift+define-role leader-impl
(start-facet get-quotes
(field [book-list (List String) (rest titles)]
[title String (first titles)])
@ -154,7 +157,7 @@
(when (> (set-count (ref nays))
(/ (set-count (ref members)) 2))
(printf "leader finds enough negative nancys for ~a\n" (ref title))
(stop poll-members (next-book)))))]))))))
(stop poll-members (next-book)))))])))))))
(define-type-alias member-role
(Role (member)
@ -167,7 +170,8 @@
(define (spawn-club-member [name : String]
[titles : (List String)])
(spawn τc
(print-role
(export-roles "member-impl.rktd"
(lift+define-role member-impl
(start-facet member
;; assert our presence
(assert (club-member name))
@ -175,7 +179,7 @@
(during (observe (book-interest $title _ _))
(define answer (member? title titles))
(printf "~a responds to suggested book ~a: ~a\n" name title answer)
(assert (book-interest title name answer)))))))
(assert (book-interest title name answer))))))))
(run-ground-dataspace τc
(spawn-seller (list (tuple "The Wind in the Willows" 5)
@ -187,3 +191,19 @@
"Encyclopaedia Brittannica"))
(spawn-club-member "tony" (list "Candide"))
(spawn-club-member "sam" (list "Encyclopaedia Brittannica" "Candide")))
(module+ test
(verify-actors (And (Eventually (A BookQuote))
(Always (Implies (A (Observe (BookQuoteT String ★/t)))
(Eventually (A BookQuote))))
(Always (Implies (A (Observe (BookInterestT String ★/t ★/t)))
(Eventually (A BookInterest)))))
leader-impl
seller-impl
member-impl))
(module+ test
(check-simulates leader-impl leader-impl)
(check-has-simulating-subgraph leader-impl leader-role)
(check-simulates seller-impl seller-impl)
(check-has-simulating-subgraph seller-impl seller-role))

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
;; adapted from section 8.3 of Tony's dissertation
@ -22,11 +22,11 @@
(Role (cell-factory)
(Reacts (Message (CreateCellT ID Value))
;; want to say that what it spawns is a Cell
(Spawn ★/t))))
(Spawns ★/t))))
(define-type-alias Reader
(Role (reader)
(Shares (Observe (Cell ID ★/t)))))
(Shares (Observe (CellT ID ★/t)))))
(define-type-alias Writer
(Role (writer)
@ -68,4 +68,4 @@
(on (asserted (cell id (bind value Value)))
(printf "Cell ~a updated to: ~a\n" id value))
(on (retracted (cell id discard))
(printf "Cell ~a deleted\n" id)))))
(printf "Cell ~a deleted\n" id)))))

View File

@ -1,6 +1,6 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require "../../drivers/tcp.rkt")
(require typed/syndicate/drivers/tcp)
;; message
(define-constructor (speak who what)
@ -26,8 +26,7 @@
(spawn chat-ds
(start-facet chat-server
;; TODO - should be during/spawn
(during (tcp-connection (bind id Symbol) (tcp-listener 5999))
(during/spawn (tcp-connection (bind id Symbol) (tcp-listener 5999))
(assert (tcp-accepted id))
(let ([me (gensym 'user)])
(assert (present me))

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(define-constructor (file name content)
#:type-constructor FileT
@ -31,4 +31,4 @@
(define-type-alias Writer
(Role (writer)
(Sends Save)
(Sends Delete)))
(Sends Delete)))

View File

@ -1,8 +1,9 @@
#lang typed/syndicate/roles
#lang typed/syndicate
;; ---------------------------------------------------------------------------------------------------
;; Protocol
#|
Conversations in the flink dataspace primarily concern two topics: presence and
task execution.
@ -114,7 +115,8 @@ JobManager and the TaskManager, and one between the TaskManager and its
TaskRunners.
|#
(define-type-alias TaskAssigner
;; I think this is wrong by explicitly requiring that the facet stop in response
(define-type-alias TaskAssigner-v1
(Role (assign)
(Shares (Observe (TaskPerformance ID ConcreteTask ★/t)))
;; would be nice to say how the TaskIDs relate to each other
@ -122,6 +124,14 @@ TaskRunners.
(Branch (Stop assign)
(Effs)))))
(define-type-alias TaskAssigner
(Role (assign)
;; would be nice to say how the TaskIDs relate to each other
(Reacts (Asserted (TaskPerformance ID ConcreteTask TaskStateDesc))
)))
(export-type "task-assigner.rktd" TaskAssigner)
(define-type-alias TaskPerformer
(Role (listen)
(During (Observe (TaskPerformance ID ConcreteTask ★/t))
@ -168,9 +178,12 @@ The JobManager then performs the job and, when finished, asserts
(printf fmt . args)
(printf "\n")))
;; ---------------------------------------------------------------------------------------------------
;; TaskRunner
(define (word-count-increment [h : WordCount]
[word : String]
-> WordCount)
@ -191,8 +204,9 @@ The JobManager then performs the job and, when finished, asserts
(define (spawn-task-runner [id : ID] [tm-id : ID])
(spawn τc
(begin
(start-facet runner
(export-roles "task-runner-impl.rktd"
(lift+define-role task-runner-impl
(start-facet runner ;; #:includes-behavior TaskPerformer
(assert (task-runner id))
(on (retracted (task-manager tm-id _))
(stop runner))
@ -209,16 +223,18 @@ The JobManager then performs the job and, when finished, asserts
(set! state (finished wc))]
[(reduce-work $left $right)
(define wc (hash-union/combine left right +))
(set! state (finished wc))]))))))
(set! state (finished wc))])))))))
;; ---------------------------------------------------------------------------------------------------
;; TaskManager
(define (spawn-task-manager [num-task-runners : Int])
(define id (gensym 'task-manager))
(spawn τc
(begin
(start-facet tm
(export-roles "task-manager-impl.rktd"
(#;begin lift+define-role task-manager-impl
(start-facet tm ;; #:includes-behavior TaskAssigner
(log "Task Manager (TM) ~a is running" id)
(during (job-manager-alive)
(log "TM ~a learns about JM" id)
@ -279,25 +295,11 @@ The JobManager then performs the job and, when finished, asserts
[OVERLOAD/ts
(set! status OVERLOAD/ts)]
[(finished discard)
(set! status st)])))))))))
(set! status st)]))))))))))
;; ---------------------------------------------------------------------------------------------------
;; JobManager
;; Task -> Bool
;; Test if the task is ready to run
(define (task-ready? [t : PendingTask] -> (Maybe ConcreteTask))
(match t
[(task $tid (map-work $s))
;; having to re-produce this is directly bc of no occurrence typing
(some (task tid (map-work s)))]
[(task $tid (reduce-work (right $v1)
(right $v2)))
(some (task tid (reduce-work v1 v2)))]
[_
none]))
;; Task Int Any -> Task
;; If the given task is waiting for this data, replace the waiting ID with the data
(define (task+data [t : PendingTask]
@ -321,6 +323,20 @@ The JobManager then performs the job and, when finished, asserts
(define l (split-at/lenient- xs n))
(tuple (first l) (second l)))
;; Task -> Bool
;; Test if the task is ready to run
(define (task-ready? [t : PendingTask] -> (Maybe ConcreteTask))
(match t
[(task $tid (map-work $s))
;; having to re-produce this is directly bc of no occurrence typing
(some (task tid (map-work s)))]
[(task $tid (reduce-work (right $v1)
(right $v2)))
(some (task tid (reduce-work v1 v2)))]
[_
none]))
(define (partition-ready-tasks [tasks : (List PendingTask)]
-> (Tuple (List PendingTask)
(List ConcreteTask)))
@ -333,6 +349,7 @@ The JobManager then performs the job and, when finished, asserts
[none
(left t)]))))
(define (input->pending-task [t : InputTask] -> PendingTask)
(match t
[(task $id (map-work $s))
@ -341,6 +358,7 @@ The JobManager then performs the job and, when finished, asserts
[(task $id (reduce-work $l $r))
(task id (reduce-work (left l) (left r)))]))
(message-struct tasks-finished : TasksFinished (id results))
;; assertions used for internal slot-management protocol
@ -353,8 +371,8 @@ The JobManager then performs the job and, when finished, asserts
(define (spawn-job-manager)
(spawn τc
(begin
(start-facet jm
(lift+define-role job-manager-impl ;; export-roles "job-manager-impl.rktd"
(start-facet jm ;; #:includes-behavior TaskAssigner
(assert (job-manager-alive))
(log "Job Manager Up")
@ -495,7 +513,7 @@ The JobManager then performs the job and, when finished, asserts
(on (realize (tasks-finished job-id $data:TaskResult))
(stop delegate-tasks
(start-facet done (assert (job-completion job-id tasks data)))))
(on (realize (task-is-ready job-id $t))
(on (realize (task-is-ready job-id $t:ConcreteTask))
(perform-task t push-results)))
(for ([t (in-list ready)])
(add-ready-task! t))))))))
@ -506,10 +524,12 @@ The JobManager then performs the job and, when finished, asserts
;; Job -> Void
(define (spawn-client [j : JobDesc])
(spawn τc
(export-roles "client-impl.rktd"
(lift+define-role client-impl
(start-facet _
(match-define (job $id $tasks) j)
(on (asserted (job-completion id tasks $data))
(printf "job done!\n~a\n" data)))))
(printf "job done!\n~a\n" data)))))))
;; ---------------------------------------------------------------------------------------------------
;; Main
@ -528,3 +548,29 @@ The JobManager then performs the job and, when finished, asserts
(spawn-task-manager 3)
(spawn-client (file->job "lorem.txt"))
(spawn-client (string->job INPUT)))
(module+ test
#;(verify-actors #;(Eventually (A (JobCompletion ID (List InputTask) TaskResult)))
(Always (Implies (A (Observe (JobCompletion ID (List InputTask) ★/t)))
(Eventually (A (JobCompletion ID (List InputTask) TaskResult)))))
job-manager-impl
task-manager-impl
client-impl)
(verify-actors (And (Always (Implies (A (Observe (TaskPerformance ID ConcreteTask ★/t)))
(Eventually (A (TaskPerformance ID ConcreteTask TaskStateDesc)))))
(Eventually (A (TaskPerformance ID ConcreteTask TaskStateDesc))))
TaskAssigner
TaskPerformer))
(module+ test
(check-simulates task-runner-impl task-runner-impl)
(check-has-simulating-subgraph task-runner-impl TaskPerformer)
(check-simulates task-manager-impl task-manager-impl)
(check-has-simulating-subgraph task-manager-impl TaskPerformer)
(check-has-simulating-subgraph task-manager-impl TaskAssigner)
(check-has-simulating-subgraph job-manager-impl TaskAssigner))
;; infinite loop?
#;(module+ test
(check-simulates job-manager-impl job-manager-impl))

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
;; Expected Output:
#|

View File

@ -0,0 +1,36 @@
#lang typed/syndicate
;; Expected Output
;; pong: 8339
(message-struct ping : Ping (v))
(message-struct pong : Pong (v))
(define-type-alias ds-type
(U (Message (Ping Int))
(Message (Pong Int))
(Observe (Ping ★/t))
(Observe (Pong ★/t))
(Observe (Observe (Ping ★/t)))))
(run-ground-dataspace ds-type
(spawn ds-type
(lift+define-role ponger
(start-facet echo
(on (message (ping $v))
(send! (pong v))))))
(spawn ds-type
(lift+define-role pinger
(start-facet serve
(on (asserted (observe (ping _)))
(send! (ping 8339)))
(on (message (pong $x))
(printf "pong: ~v\n" x))))))
(module+ test
(verify-actors (And (Eventually (M (Ping Int)))
(Eventually (M (Pong Int)))
(Always (Implies (M (Ping Int))
(Eventually (M (Pong Int))))))
pinger
ponger))

View File

@ -1,8 +1,8 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(provide a-fun)
(define (a-fun [x : Int] -> Int)
(+ x 1))
#;(a-fun 5)
#;(a-fun 5)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
;; Expected Output:
#|

View File

@ -1,13 +1,16 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require-struct msg #:as Msg
#:from "driver.rkt")
(define m (msg 1 "hi"))
(msg-in m)
(msg-out m)
(match m
[(msg (bind x Int) discard)
(displayln x)])
;; error: msg/checked: arity mismatch
#;(msg 1 2 3)
#;(msg 1 2 3)

View File

@ -0,0 +1,12 @@
#lang racket
(struct egg (size day) #:transparent)
(provide (except-out (struct-out egg)
egg-size
egg-day))
(struct chicken (eggs) #:transparent)
(provide chicken)

View File

@ -0,0 +1,18 @@
#lang typed/syndicate/roles
(require-struct egg #:as Egg #:from "lib.rkt" #:omit-accs)
(define e (egg 5 "Sun"))
(match e
[(egg $sz $d)
(displayln sz)
(displayln d)])
(require-struct chicken #:as Chicken #:from "lib.rkt" #:omit-accs)
(define c (chicken (list e e e)))
(match c
[(chicken $eggs)
(displayln eggs)])

View File

@ -0,0 +1,5 @@
#lang typed/syndicate
(require/typed "lib.rkt" [x : Int])
(displayln (+ x 1))

View File

@ -0,0 +1,8 @@
#lang typed/syndicate
(require/typed "lib.rkt"
[#:opaque Vec #:arity = 3]
[ones : (Vec Int Int Int)]
[vec+ : (→fn (Vec Int Int Int) (Vec Int Int Int) (Vec Int Int Int))])
(vec+ ones ones)

View File

@ -0,0 +1,8 @@
#lang typed/syndicate
(require/typed "lib.rkt"
[#:opaque Vec]
[ones : Vec]
[vec+ : (→fn Vec Vec Vec)])
(vec+ ones ones)

View File

@ -0,0 +1,13 @@
#lang racket
(provide ones
vec+)
(struct vec (x y z) #:transparent)
(define ones (vec 1 1 1))
(define (vec+ v1 v2)
(vec (+ (vec-x v1) (vec-x v2))
(+ (vec-y v1) (vec-y v2))
(+ (vec-z v1) (vec-z v2))))

View File

@ -0,0 +1,5 @@
#lang typed/syndicate
(require "provides.rkt")
(a-fun 5)

View File

@ -1,57 +0,0 @@
#lang typed/syndicate/roles
;; Expected Output
;; 0
;; 70
;; #f
(define-constructor (account balance)
#:type-constructor AccountT
#:with Account (AccountT Int)
#:with AccountRequest (AccountT ★/t))
(define-constructor (deposit amount)
#:type-constructor DepositT
#:with Deposit (DepositT Int)
#:with DepositRequest (DepositT ★/t))
(define-type-alias ds-type
(U Account
(Observe AccountRequest)
(Observe (Observe AccountRequest))
Deposit
(Observe DepositRequest)
(Observe (Observe DepositRequest))))
(define-type-alias account-manager-role
(Role (account-manager)
(Shares Account)
(Reacts (Know (Deposit Int)))))
(define-type-alias client-role
(Role (client)
(Reacts (Know Account))))
(run-ground-dataspace ds-type
(spawn ds-type
(print-role
(start-facet account-manager
(field [balance Int 0])
(assert (account (ref balance)))
(on (asserted (deposit (bind amount Int)))
(set! balance (+ (ref balance) amount))))))
(spawn ds-type
(print-role
(start-facet observer
(on (asserted (account (bind amount Int)))
(displayln amount)))))
(spawn ds-type
(print-role
(start-facet buyer
(on (asserted (observe (deposit discard)))
(start-facet deposits
(assert (deposit 100))
(assert (deposit -30))))))))

View File

@ -1,20 +0,0 @@
#lang typed/syndicate/roles
;; Expected Output
;; pong: 8339
(define-type-alias ds-type
(U (Message (Tuple String Int))
(Observe (Tuple String ★/t))))
(run-ground-dataspace ds-type
(spawn ds-type
(start-facet echo
(on (message (tuple "ping" (bind x Int)))
(send! (tuple "pong" x)))))
(spawn ds-type
(start-facet serve
(on start
(send! (tuple "ping" 8339)))
(on (message (tuple "pong" (bind x Int)))
(printf "pong: ~v\n" x)))))

View File

@ -1,5 +0,0 @@
#lang typed/syndicate/roles
(require/typed "lib.rkt" [x : Int])
(displayln (+ x 1))

View File

@ -1,5 +0,0 @@
#lang typed/syndicate/roles
(require "provides.rkt")
(a-fun 5)

View File

@ -1,150 +0,0 @@
#lang typed/syndicate/roles
;; Expected Output
;; Completed Order:
;; Catch 22
;; 10001483
;; March 9th
(define-constructor (price v)
#:type-constructor PriceT
#:with Price (PriceT Int))
(define-constructor (out-of-stock)
#:type-constructor OutOfStockT
#:with OutOfStock (OutOfStockT))
(define-type-alias QuoteAnswer
(U Price OutOfStock))
(define-constructor (quote title answer)
#:type-constructor QuoteT
#:with Quote (QuoteT String QuoteAnswer)
#:with QuoteRequest (Observe (QuoteT String ★/t))
#:with QuoteInterest (Observe (QuoteT ★/t ★/t)))
(define-constructor (split-proposal title price contribution accepted)
#:type-constructor SplitProposalT
#:with SplitProposal (SplitProposalT String Int Int Bool)
#:with SplitRequest (Observe (SplitProposalT String Int Int ★/t))
#:with SplitInterest (Observe (SplitProposalT ★/t ★/t ★/t ★/t)))
(define-constructor (order-id id)
#:type-constructor OrderIdT
#:with OrderId (OrderIdT Int))
(define-constructor (delivery-date date)
#:type-constructor DeliveryDateT
#:with DeliveryDate (DeliveryDateT String))
(define-type-alias (Maybe t)
(U t Bool))
(define-constructor (order title price id delivery-date)
#:type-constructor OrderT
#:with Order (OrderT String Int (Maybe OrderId) (Maybe DeliveryDate))
#:with OrderRequest (Observe (OrderT String Int ★/t ★/t))
#:with OrderInterest (Observe (OrderT ★/t ★/t ★/t ★/t)))
(define-type-alias ds-type
(U ;; quotes
Quote
QuoteRequest
(Observe QuoteInterest)
;; splits
SplitProposal
SplitRequest
(Observe SplitInterest)
;; orders
Order
OrderRequest
(Observe OrderInterest)))
(define-type-alias seller-role
(Role (seller)
(Reacts (Asserted (Observe (QuoteT String ★/t)))
(Role (_)
(Shares (QuoteT String Int))))))
(run-ground-dataspace ds-type
;; seller
(spawn ds-type
(start-facet _
(field [book (Tuple String Int) (tuple "Catch 22" 22)]
[next-order-id Int 10001483])
(on (asserted (observe (quote (bind title String) discard)))
(start-facet x
(on (retracted (observe (quote title discard)))
(stop x))
(define answer
(match title
["Catch 22"
(price 22)]
[_
(out-of-stock)]))
(assert (quote title answer))))
(on (asserted (observe (order (bind title String) (bind offer Int) discard discard)))
(start-facet x
(on (retracted (observe (order title offer discard discard)))
(stop x))
(let ([asking-price 22])
(if (and (equal? title "Catch 22") (>= offer asking-price))
(let ([id (ref next-order-id)])
(set! next-order-id (+ 1 id))
(assert (order title offer (order-id id) (delivery-date "March 9th"))))
(assert (order title offer #f #f))))))))
;; buyer A
(spawn ds-type
(start-facet buyer
(field [title String "Catch 22"]
[budget Int 1000])
(on (asserted (quote (ref title) (bind answer QuoteAnswer)))
(match answer
[(out-of-stock)
(stop buyer)]
[(price (bind amount Int))
(start-facet negotiation
(field [contribution Int (/ amount 2)])
(on (asserted (split-proposal (ref title) amount (ref contribution) (bind accept? Bool)))
(if accept?
(stop buyer)
(if (> (ref contribution) (- amount 5))
(stop negotiation (displayln "negotiation failed"))
(set! contribution
(+ (ref contribution) (/ (- amount (ref contribution)) 2)))))))]))))
;; buyer B
(spawn ds-type
(start-facet buyer-b
(field [funds Int 5])
(on (asserted (observe (split-proposal (bind title String) (bind price Int) (bind their-contribution Int) discard)))
(let ([my-contribution (- price their-contribution)])
(cond
[(> my-contribution (ref funds))
(start-facet decline
(assert (split-proposal title price their-contribution #f))
(on (retracted (observe (split-proposal title price their-contribution discard)))
(stop decline)))]
[#t
(start-facet accept
(assert (split-proposal title price their-contribution #t))
(on (retracted (observe (split-proposal title price their-contribution discard)))
(stop accept))
(on start
(spawn ds-type
(start-facet purchase
(on (asserted (order title price (bind order-id? (Maybe OrderId)) (bind delivery-date? (Maybe DeliveryDate))))
(match (tuple order-id? delivery-date?)
[(tuple (order-id (bind id Int)) (delivery-date (bind date String)))
;; complete!
(begin (displayln "Completed Order:")
(displayln title)
(displayln id)
(displayln date)
(stop purchase))]
[discard
(begin (displayln "Order Rejected")
(stop purchase))]))))))])))))
)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
;; Expected Output
;; f: 0

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(run-ground-dataspace Int
(spawn Int

View File

@ -0,0 +1,33 @@
#lang typed/syndicate
;; Expected Output
;; +parent
;; +GO
;; +ready
;; -parent
;; -GO
;; -ready
(define-type-alias ds-type
(U (Tuple String) (Observe (Tuple ★/t))))
(run-ground-dataspace ds-type
(spawn ds-type
(start-facet parent
(assert (tuple "parent"))
(during/spawn (tuple "GO")
(assert (tuple "ready")))
(on (asserted (tuple "ready"))
(stop parent))))
(spawn ds-type
(start-facet flag
(assert (tuple "GO"))
(on (retracted (tuple "parent"))
(stop flag))))
(spawn ds-type
(start-facet obs
(during (tuple (bind s String))
(on start
(printf "+~a\n" s))
(on stop
(printf "-~a\n" s))))))

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
;; Expected Output
;; +GO

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
;; Expected Output
;; adding key2 -> 88

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
;; Expected Output
;; size: 0

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
;; Expected Output
;; query: 0

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
;; Expected Output:
;; +42

View File

@ -0,0 +1,7 @@
#lang typed/syndicate/roles
(require "typed-out.rkt")
(define c : (Cow Int) (cow 5))
(cow-moos c)

View File

@ -0,0 +1,7 @@
#lang typed/syndicate/roles
(require "struct-out.rkt")
(happy-days (happy 5))
(define classic : (Happy Int) (happy 100))

View File

@ -0,0 +1,5 @@
#lang typed/syndicate/roles
(provide (struct-out happy))
(define-constructor* (happy : Happy days))

View File

@ -0,0 +1,5 @@
#lang typed/syndicate/roles
(require-struct cow #:as Cow #:from "untyped.rkt")
(provide (struct-out cow))

View File

@ -0,0 +1,5 @@
#lang racket
(provide (struct-out cow))
(struct cow (moos) #:transparent)

View File

@ -20,14 +20,14 @@
(define-constructor (quote title answer)
#:type-constructor QuoteT
#:with Quote (QuoteT String QuoteAnswer)
#:with QuoteRequest (Observe (QuoteT String ))
#:with QuoteInterest (Observe (QuoteT )))
#:with QuoteRequest (Observe (QuoteT String /t))
#:with QuoteInterest (Observe (QuoteT /t /t)))
(define-constructor (split-proposal title price contribution accepted)
#:type-constructor SplitProposalT
#:with SplitProposal (SplitProposalT String Int Int Bool)
#:with SplitRequest (Observe (SplitProposalT String Int Int ))
#:with SplitInterest (Observe (SplitProposalT )))
#:with SplitRequest (Observe (SplitProposalT String Int Int /t))
#:with SplitInterest (Observe (SplitProposalT /t /t /t /t)))
(define-constructor (order-id id)
#:type-constructor OrderIdT
@ -40,11 +40,11 @@
(define-type-alias (Maybe t)
(U t Bool))
(define-constructor (order title price id delivery-date)
(define-constructor (order title price oid delivery-date)
#:type-constructor OrderT
#:with Order (OrderT String Int (Maybe OrderId) (Maybe DeliveryDate))
#:with OrderRequest (Observe (OrderT String Int ))
#:with OrderInterest (Observe (OrderT )))
#:with OrderRequest (Observe (OrderT String Int /t /t))
#:with OrderInterest (Observe (OrderT /t /t /t /t)))
(define-type-alias ds-type
(U ;; quotes
@ -60,88 +60,104 @@
OrderRequest
(Observe OrderInterest)))
(dataspace ds-type
(define-type-alias seller-role
(Role (seller)
(During (Observe (QuoteT String ★/t))
(Shares (QuoteT String QuoteAnswer)))
#;(Reacts (Asserted (Observe (QuoteT String ★/t)))
(Role (_)
;; QuoteAnswer was originally, erroneously, Int
(Shares (QuoteT String QuoteAnswer))))))
(run-ground-dataspace ds-type
;; seller
(spawn ds-type
(facet _
(fields [book (Tuple String Int) (tuple "Catch 22" 22)]
[next-order-id Int 10001483])
(on (asserted (observe (quote (bind title String) discard)))
(facet x
(fields)
(on (retracted (observe (quote title discard)))
(stop x (begin)))
(match title
["Catch 22"
(assert (quote title (price 22)))]
[discard
(assert (quote title (out-of-stock)))])))
(on (asserted (observe (order (bind title String) (bind offer Int) discard discard)))
(facet x
(fields)
(on (retracted (observe (order title offer discard discard)))
(stop x (begin)))
(let [asking-price 22]
(if (and (equal? title "Catch 22") (>= offer asking-price))
(let [id (ref next-order-id)]
(begin (set! next-order-id (+ 1 id))
(assert (order title offer (order-id id) (delivery-date "March 9th")))))
(assert (order title offer #f #f))))))))
(lift+define-role seller-impl
(start-facet _ ;; #:implements seller-role
(field [book (Tuple String Int) (tuple "Catch 22" 22)]
[next-order-id Int 10001483])
(on (asserted (observe (quote (bind title String) discard)))
(start-facet x
(on (retracted (observe (quote title discard)))
(stop x))
(define answer
(match title
["Catch 22"
(price 22)]
[_
(out-of-stock)]))
(assert (quote title answer))))
(on (asserted (observe (order (bind title String) (bind offer Int) discard discard)))
(start-facet x
(on (retracted (observe (order title offer discard discard)))
(stop x))
(let ([asking-price 22])
(if (and (equal? title "Catch 22") (>= offer asking-price))
(let ([id (ref next-order-id)])
(set! next-order-id (+ 1 id))
(assert (order title offer (order-id id) (delivery-date "March 9th"))))
(assert (order title offer #f #f)))))))))
;; buyer A
(spawn ds-type
(facet buyer
(fields [title String "Catch 22"]
[budget Int 1000])
(on (asserted (quote (ref title) (bind answer QuoteAnswer)))
(match answer
[(out-of-stock)
(stop buyer (begin))]
[(price (bind amount Int))
(facet negotiation
(fields [contribution Int (/ amount 2)])
(on (asserted (split-proposal (ref title) amount (ref contribution) (bind accept? Bool)))
(if accept?
(stop buyer (begin))
(if (> (ref contribution) (- amount 5))
(stop negotiation (displayln "negotiation failed"))
(set! contribution
(+ (ref contribution) (/ (- amount (ref contribution)) 2)))))))]))))
(lift+define-role buyer-a-impl
(start-facet buyer
(field [title String "Catch 22"]
[budget Int 1000])
(on (asserted (quote (ref title) (bind answer QuoteAnswer)))
(match answer
[(out-of-stock)
(stop buyer)]
[(price (bind amount Int))
(start-facet negotiation
(field [contribution Int (/ amount 2)])
(on (asserted (split-proposal (ref title) amount (ref contribution) (bind accept? Bool)))
(if accept?
(stop buyer)
(if (> (ref contribution) (- amount 5))
(stop negotiation (displayln "negotiation failed"))
(set! contribution
(+ (ref contribution) (/ (- amount (ref contribution)) 2)))))))])))))
;; buyer B
(spawn ds-type
(facet buyer-b
(fields [funds Int 5])
(on (asserted (observe (split-proposal (bind title String) (bind price Int) (bind their-contribution Int) discard)))
(let [my-contribution (- price their-contribution)]
(cond
[(> my-contribution (ref funds))
(facet decline
(fields)
(assert (split-proposal title price their-contribution #f))
(on (retracted (observe (split-proposal title price their-contribution discard)))
(stop decline (begin))))]
[#t
(facet accept
(fields)
(assert (split-proposal title price their-contribution #t))
(on (retracted (observe (split-proposal title price their-contribution discard)))
(stop accept (begin)))
(on start
(spawn ds-type
(facet purchase
(fields)
(on (asserted (order title price (bind order-id? (Maybe OrderId)) (bind delivery-date? (Maybe DeliveryDate))))
(match (tuple order-id? delivery-date?)
[(tuple (order-id (bind id Int)) (delivery-date (bind date String)))
;; complete!
(begin (displayln "Completed Order:")
(displayln title)
(displayln id)
(displayln date)
(stop purchase (begin)))]
[discard
(begin (displayln "Order Rejected")
(stop purchase (begin)))]))))))])))))
)
(lift+define-role buyer-b-impl
(start-facet buyer-b
(field [funds Int 5])
(on (asserted (observe (split-proposal (bind title String) (bind price Int) (bind their-contribution Int) discard)))
(let ([my-contribution (- price their-contribution)])
(cond
[(> my-contribution (ref funds))
(start-facet decline
(assert (split-proposal title price their-contribution #f))
(on (retracted (observe (split-proposal title price their-contribution discard)))
(stop decline)))]
[#t
(start-facet accept
(assert (split-proposal title price their-contribution #t))
(on (retracted (observe (split-proposal title price their-contribution discard)))
(stop accept))
(on start
(spawn ds-type
(start-facet purchase
(on (asserted (order title price (bind order-id? (Maybe OrderId)) (bind delivery-date? (Maybe DeliveryDate))))
(match (tuple order-id? delivery-date?)
[(tuple (order-id (bind id Int)) (delivery-date (bind date String)))
;; complete!
(begin (displayln "Completed Order:")
(displayln title)
(displayln id)
(displayln date)
(stop purchase))]
[discard
(begin (displayln "Order Rejected")
(stop purchase))]))))))]))))))
)
(module+ test
(check-simulates seller-impl seller-impl)
;; found a bug in spec, see seller-role above
(check-simulates seller-impl seller-role)
(check-simulates buyer-a-impl buyer-a-impl)
(check-simulates buyer-b-impl buyer-b-impl))

View File

@ -1,8 +1,12 @@
#lang info
(define scribblings '(("scribblings/typed-syndicate.scrbl" ())))
(define compile-omit-paths
'("examples"
"tests"))
(define test-omit-paths
'("examples/roles/chat-tcp2.rkt"))
;; a number of the examples use SPIN for model checking which I need
;; to figure out how to get working on the package server
'("examples/"))

View File

@ -0,0 +1,776 @@
#lang scribble/manual
@(require (for-label (only-in racket struct)
typed/syndicate/roles)
(prefix-in racket: (for-label racket))
(prefix-in untyped: (for-label syndicate/actor)))
@title{Typed Syndicate}
@defmodule[typed/syndicate/roles]
@section{Overview}
@section{Types}
@deftogether[(@defidform[Int]
@defidform[Bool]
@defidform[String]
@defidform[ByteString]
@defidform[Symbol])]{
Base types.
}
@defform[(U type ...)]{
The type representing the union of @racket[type ...].
}
@defidform[⊥]{
An alias for @racket[(U)].
}
@defidform[★/t]{
The type representing any possible assertion, and, in an @racket[AssertionSet],
the possibility for an infinite set of assertions.
}
@defidform[Discard]{
The type of @racket[_] patterns.
}
@defform[(Bind type)]{
The type of @racket[$] patterns.
}
@defidform[FacetName]{
The type associated with identifiers bound by @racket[start-facet].
}
@defform[(Role (x) type ...)]{
The type of a facet named @racket[x] and endpoints described by @racket[type
...].
}
@defform[(Stop X type ...)]{
The type of a @racket[stop] action.
}
@defform[(Field type)]{
The type of a field containing values of @racket[type].
}
@defform[(Shares type)]{
The type of an @racket[assert] endpoint.
}
@defform[#:literals (OnStart OnStop Asserted Retracted)
(Reacts EventDesc type ...)
#:grammar
[(EventDesc (code:line OnStart)
(code:line OnStart)
(code:line (Asserted event-type))
(code:line (Retracted event-type)))]]{
The type of a @racket[on] endpoint that reacts to events described by
@racket[EventDesc] with the behavior given by @racket[type ...].
}
@deftogether[(@defidform[OnStart]
@defidform[OnStop]
@defform[(Asserted type)]
@defform[(Retracted type)])]{
See @racket[Reacts].
}
@defform[(Actor type)]{
The type of an actor that operates in a dataspace with a certain communication
@racket[type].
}
@defform[(ActorWithRole comm-type behavior-type)]{
An @racket[Actor] type with the additional @racket[behavior-type] describing the
actor's behavior in terms of a @racket[Role].
}
@defform[(Sends type)]{
The type of a @racket[send!] action.
}
@defform[(Realize type)]{
The type of a @racket[realize!] action.
}
@deftogether[(@defform[(Branch type ...)]
@defform[(Effs type ...)])]{
Types that may arise in descriptions in @racket[Role] types due to branching and
sequencing.
}
@defform[(Tuple type ...)]{
The type of @racket[tuple] expressions.
}
@defidform[Unit]{
An alias for @racket[(Tuple)].
}
@defform[(AssertionSet type)]{
The type for a set of assertions of a certain @racket[type]. Note that these are
not interoperable with the general purpose @racket[set] data structures.
}
@defform[(∀ (X ...) type)]{
Universal quantification over types.
}
@defform[#:literals (Computation Value Endpoints Roles Spawns)
(→ type ... (Computation (Value result-type)
(Endpoints ep-type ...)
(Roles role-type ...)
(Spawns spawn-type ...)))]{
The type of a function with parameters @racket[type ...] that returns @racket[result-type]. The type includes the effects of any actions performed by the function:
@itemlist[
@item{@racket[Endpoints]: includes any endpoint installation effects, such as from @racket[assert] and @racket[on].}
@item{@racket[Roles]: includes any script action effects, such as from @racket[start-facet], @racket[stop], and @racket[send!].}
@item{@racket[Spawns]: includes descriptions of any @racket[spawn] actions.}
]
}
@defform[(→fn type-in ... type-out)]{
Shorthand for a @racket[→] type with no effects.
}
@defform[(proc maybe-quantifiers type-in ... maybe-arrow type-out
maybe-endpoints
maybe-roles
maybe-spawns)
#:grammar
[(maybe-quantifiers (code:line)
(code:line #:forall (X ...)))
(maybe-arrow (code:line)
(code:line →)
(code:line ->))
(maybe-endpoints (code:line)
(code:line #:endpoints (e ...)))
(maybe-roles (code:line)
(code:line #:roles (r ...)))
(maybe-spawns (code:line)
(code:line #:spawns (s ...)))]]{
A more convenient notation for writing (potentially polymorphic) function types
with effects. Shorthand for @racket[(∀ (X ...) (→ type-in ... (Computation
(Value type-out) (Endpoints e ...) (Roles r ...) (Spawns s ...))))].
}
@deftogether[(@defform[(Computation type ...)]
@defform[(Value type)]
@defform[(Endpoints type)]
@defform[(Roles type)]
@defform[(Spawns type)])]{
See @racket[→].
}
@section{User Defined Types}
@defform*[[(define-type-alias id type)
(define-type-alias (ty-cons-id arg-id ...) type)]]{
Define @racket[id] to be the same as @racket[type], or create a type constructor
@racket[(ty-cons-id ty ...)] whose meaning is @racket[type] with references to
@racket[arg-id ...] replaced by @racket[ty ...].
}
@defform[(define-constructor (ctor-id slot-id ...)
maybe-type-ctor
maybe-alias ...)
#:grammar
[(maybe-type-ctor (code:line)
(code:line #:type-constructor type-ctor-id))
(maybe-alias (code:line)
(code:line #:with alias alias-body))]]{
Defines a container analagous to a prefab @racket[struct]. Includes accessor
functions for each @racket[slot-id]. (But not, presently, a predicate function).
When a @racket[type-ctor-id] is provided, the type of such structures is
@racket[(type-ctor-id type ...)], where each @racket[type] describes the value
of the corresponding slot. When not provided, the type constructor is named by
appending @racket["/t"] to @racket[ctor-id].
Each @racket[alias] and @racket[alias-body] creates an instance of
@racket[define-type-alias].
}
@defform[#:literals (:)
(define-constructor* (ctor-id : type-ctor-id slot-id ...)
maybe-alias ...)]{
An abbreviated form of @racket[define-constructor].
}
@defform[#:literals (:)
(assertion-struct ctor-id : type-ctor-id (slot-id ...))]{
An abbreviated form of @racket[define-constructor].
}
@defform[#:literals (:)
(message-struct ctor-id : type-ctor-id (slot-id ...))]{
An abbreviated form of @racket[define-constructor].
}
@section{Actor Forms}
@defform[(run-ground-dataspace type expr ...)]{
Starts a ground, i.e. main, dataspace of the program, with the given
communication @racket[type] and initial actors spawned by @racket[expr ...].
}
@defform[(spawn maybe-type s)
#:grammar
[(maybe-type (code:line)
(code:line type))]]{
Spawns an actor with behavior given by @racket[s]. The @racket[type] gives the
communication type of the enclosing dataspace. When absent, @racket[type] is
supplied by the nearest lexically enclosing @racket[spawn] or @racket[dataspace]
form, if any exist.
}
@defform[(dataspace type expr ...)]{
Spawns a dataspace with communication type @racket[type] as a child of the
dataspace enclosing the executing actor. The script @racket[expr ...] spawns the
initial actors of the new dataspace.
}
@defform[(start-facet id maybe-spec expr ...+)
#:grammar
[(maybe-spec (code:line)
(code:line #:implements type)
(code:line #:includes-behavior type))]]{
Start a facet with name @racket[id] and endpoints installed through the
evaluation of @racket[expr ...].
}
@defform[(stop id expr ...)]{
Terminate the facet @racket[id] with continuation script @racket[expr ...]. Any
facets started by the continuation script survive the termination of facet
@racket[id].
}
@defform[#:literals (start stop message asserted retracted _ $)
(on event-description body ...+)
#:grammar
[(event-description (code:line start)
(code:line stop)
(code:line (message pattern))
(code:line (asserted pattern))
(code:line (retracted pattern)))
(pattern (code:line _)
(code:line ($ id type))
(code:line ($ id))
(code:line $id)
(code:line $id:type)
(code:line (ctor pattern ...))
(code:line expr))]]{
Creates an event handler endpoint that responds to the event specified by
@racket[event-description]. Executes the @racket[body ...] for each matching
event, with any pattern variables bound to their matched value.
Patterns have the following meanings:
@itemlist[
@item{@racket[_] matches anything.}
@item{@racket[($ id type)] matches any value and binds it to @racket[id] with
assumed type @racket[type].}
@item{@racket[($ id)] is like @racket[($ id type)], but attempts to use the
current communication type to fill in the @racket[type] of potential matches.
May raise an error if no suitable communication type is in scope.}
@item{@racket[(? pred pattern)] matches values where @racket[(pred val)] is not
@racket[#f] and that match @racket[pattern].}
@item{@racket[$id:type] is shorthand for @racket[($ id type)].}
@item{@racket[$id] is shorthand for @racket[($ id)].}
@item{@racket[(ctor pat ...)] matches values built by applying the constructor
@racket[ctor] to values matching @racket[pat ...]. @racket[ctor] is usually
a @racket[struct] name.}
@item{@racket[expr] patterns match values that are @racket[equal?] to
@racket[expr].}
]
}
@defform[(on-start expr ...+)]{
Shorthand for @racket[(on start expr ...)].
}
@defform[(on-stop expr ...+)]{
Shorthand for @racket[(on stop expr ...)].
}
@defform[(assert expr)]{
Creates an assertion endpoint with the value of @racket[expr].
}
@defform[(know expr)]{
Creates an internal assertion endpoint with the value of @racket[expr].
}
@defform[(send! expr)]{
Broadcast a dataspace message with the value of @racket[expr].
}
@defform[(realize! expr)]{
Broadcast an actor-internal message with the value of @racket[expr].
}
@defform[#:literals (:)
(field [id maybe-type expr] ...)
#:grammar
[(maybe-type (code:line)
(code:line type)
(code:line : type))]]{
Defines fields of type @racket[type] with names @racket[id] and initial values
@racket[expr]. If @racket[type] is not provided, the type of the initial
expression is used as the type of the field.
}
@defform[(ref id)]{
Reference the @racket[field] named @racket[id].
}
@defform[(set! id expr)]{
Update the value the @racket[field] named @racket[id].
}
@defform[(begin/dataflow expr ...+)]{
Evaluate and perform the script @racket[expr ...], and then again each time a
field referenced by the script updates.
}
@defform[(during pattern expr ...+)]{
Engage in behavior for the duration of a matching assertion. The syntax of
@racket[pattern] is the same as described by @racket[on].
}
@defform[(during/spawn pattern expr ...+)]{
Like @racket[during], but spawns an actor for the behavior @racket[expr ...].
}
@defform[(define/query-value name absent-expr pattern expr
maybe-on-add
maybe-on-remove)
#:grammar
[(maybe-on-add (code:line)
(code:line #:on-add on-add-expr))
(maybe-on-remove (code:line)
(code:line #:on-remove on-remove-expr))]]{
Equivalent to the untyped @racket[untyped:define/query-value].
}
@defform[(define/query-set name pattern expr
maybe-on-add
maybe-on-remove)
#:grammar
[(maybe-on-add (code:line)
(code:line #:on-add on-add-expr))
(maybe-on-remove (code:line)
(code:line #:on-remove on-remove-expr))]]{
Equivalent to the untyped @racket[untyped:define/query-set].
}
@defform[(define/query-hash name pattern key-expr value-expr
maybe-on-add
maybe-on-remove)
#:grammar
[(maybe-on-add (code:line)
(code:line #:on-add on-add-expr))
(maybe-on-remove (code:line)
(code:line #:on-remove on-remove-expr))]]{
Equivalent to the untyped @racket[untyped:define/query-hash].
}
@defform[(define/dataflow name maybe-type expr)
#:grammar
[(maybe-type (code:line)
(code:line type))]]{
Define a @racket[field] named @racket[name], whose value is reevaluated to the
result of @racket[expr] each time any referenced field changes. When
@racket[type] is not supplied, the field has the type of the given
@racket[expr].
}
@section{Expressions}
@defform*[#:literals (:)
[(ann expr : type)
(ann expr type)]]{
Ensure that @racket[expr] has the given @racket[type].
}
@defform[(if test-expr then-expr else-expr)]{
The same as Racket's @racket[racket:if].
}
@deftogether[(@defform[(cond [test-expr body-expr ...+] ...+)]
@defthing[else Bool #:value #t])]{
Like Racket's @racket[racket:cond].
}
@defform[(when test-expr expr)]{
Like Racket's @racket[racket:when], but results in @racket[#f] when
@racket[test-expr] is @racket[#f].
}
@defform[(unless test-expr expr)]{
Like Racket's @racket[racket:unless], but results in @racket[#f] when
@racket[test-expr] is @racket[#f].
}
@defform[(let ([id expr] ...) body ...+)]{
The same as Racket's @racket[racket:let].
}
@defform[(let* ([id expr] ...) body ...+)]{
The same as Racket's @racket[racket:let*].
}
@defform[#:literals (:)
(lambda ([x opt-: type] ...) expr ...+)
#:grammar
[(opt-: (code:line)
(code:line :))]]{
Constructsa an anonymous function.
}
@defidform[λ]{Synonym for @racket[lambda].}
@defform[(Λ (X ...) expr)]{
Parametric abstraction over type variables @racket[X ...].
}
@defform[(inst expr type ...)]{
Instantiates the type variables @racket[X ...] with @racket[type ...], where
@racket[expr] has type @racket[(∀ (X ...) t)].
}
@defform*[#:literals (: → -> ∀)
[(define id : type expr)
(define id expr)
(define (id [arg-id opt-: arg-type] ... opt-res-ty) expr ...+)
(define (∀ (X ...) (id [arg-id opt-: arg-type] ... opt-res-ty)) expr ...+)]
#:grammar
[(opt-: (code:line) (code:line :))
(opt-res-ty (code:line)
(code:line arr res-type))
(arr (code:line →) (code:line ->))]]{
Define a constant or a (potentially polymorphic) function. Note that the
function name @racket[id] is @emph{not} bound in the body.
}
@defform[(define-tuple (id ...) expr)]{
Define @racket[id ...] to each of the slots of the tuple produced by
@racket[expr].
}
@defform[(match-define pattern expr)]{
Define the binders of @racket[pattern] to the matching values of @racket[expr].
}
@defform[(begin expr ...+)]{
Sequencing form whose value and type is that of the final @racket[expr].
}
@defform[(block expr ...+)]{
Like @racket[begin], but also introduces a definition context for its body.
}
@defform[(match expr [pattern body-expr ...+] ...+)]{
Like Racket's @racket[racket:match] but with the pattern syntax described by
@racket[on].
}
@defform[(tuple expr ...)]{
Constructs a tuple of arbitrary arity.
}
@defform[(select i expr)]{
Extract the @racket[i]th element of a @racket[tuple].
}
@defthing[unit Unit #:value (tuple)]
@defform[(error format-expr arg-expr ...)]{
Raises an exception using @racket[format-expr] as a format string together with
@racket[arg-expr ...].
}
@deftogether[(
@defthing[+ (→fn Int Int Int)]
@defthing[- (→fn Int Int Int)]
@defthing[* (→fn Int Int Int)]
@defthing[< (→fn Int Int Bool)]
@defthing[> (→fn Int Int Bool)]
@defthing[<= (→fn Int Int Bool)]
@defthing[>= (→fn Int Int Bool)]
@defthing[= (→fn Int Int Bool)]
@defthing[even? (→fn Int Bool)]
@defthing[odd? (→fn Int Bool)]
@defthing[add1 (→fn Int Int)]
@defthing[sub1 (→fn Int Int)]
@defthing[max (→fn Int Int Int)]
@defthing[min (→fn Int Int Int)]
@defthing[zero? (→fn Int Bool)]
@defthing[positive? (→fn Int Bool)]
@defthing[negative? (→fn Int Bool)]
@defthing[current-inexact-milleseconds? (→fn Int)]
@defthing[string=? (→fn String String Bool)]
@defthing[bytes->string/utf-8 (→fn ByteString String)]
@defthing[string->bytes/utf-8 (→fn String ByteString)]
@defthing[gensym (→fn Symbol Symbol)]
@defthing[symbol->string (→fn Symbol String)]
@defthing[string->symbol (→fn String Symbol)]
@defthing[not (→fn Bool Bool)]
@defform[(/ e1 e2)]
@defform[(and e ...)]
@defform[(or e ...)]
@defform[(equal? e1 e2)]
@defform[(displayln e)]
@defform[(printf fmt-expr val-expr ...)]
@defform[(~a e ...)]
)]{
Primitive operations imported from Racket.
}
@defform[#:literals (:)
(for/fold ([acc-id maybe-:ty acc-expr] ...+)
(for-clause ...)
body-expr ...+)
#:grammar
[(maybe-:ty (code:line)
(code:line : acc-type))
(for-clause (code:line [id seq-expr])
(code:line [id : type seq-expr])
(code:line [(k-id v-id) hash-expr])
(code:line #:when test-expr)
(code:line #:unless test-expr)
(code:line #:break test-expr))]]{
Similar to Racket's @racket[racket:for/fold].
When more than one @racket[acc-id] is used, the body of the loop must evaluate
to a @racket[tuple] with one value for each accumulator, with the final tuple
also being the result of the entire expression.
Each @racket[seq-expr] should be of type @racket[Sequence], though expressions
of type @racket[List] and @racket[Set] are automatically converted.
}
@deftogether[(
@defform[(for/list (for-clause ...) body ...+)]
@defform[(for/set (for-clause ...) body ...+)]
@defform[(for/sum (for-clause ...) body ...+)]
@defform[(for (for-clause ...) body ...+)]
@defform[(for/first (for-clause ...) body ...+)]
)]{
Like their Racket counterparts. See @racket[for/fold] for the description of
@racket[for-clause].
Unlike @racket[racket:for/first], @racket[for/first] returns a @racket[Maybe]
value to indicate success/failure.
}
@section{Require & Provide}
@defform[(struct-out ctor-id)]{
}
@subsection{Requiring From Outside Typed Syndicate}
@defform[#:literals (:)
(require/typed lib clause ...)
#:grammar
[(clause (code:line [id : type])
(code:line opaque))
(opaque (code:line [#:opaque type-name])
(code:line [#:opaque type-name #:arity op arity-nat]))
(opaque (code:line =) (code:line >) (code:line >=))]]{
Import and assign types to bindings from outside Typed Syndicate.
Note that @emph{unlike} Typed Racket, Typed Syndicate does not attach contracts
to imported bindings.
An @racket[#:opaque] declaration defines a type @racket[type-name] (or, in the
@racket[#:arity] case, a type constructor) that may be used to describe imports
but otherwise has no other operations.
}
@defform[(require-struct ctor-id #:as ty-ctor-id #:from lib maybe-omit-accs)
#:grammar
[(maybe-omit-accs (code:line)
(code:line #:omit-accs))]]{
Import a Racket @racket[struct] named @racket[ctor-id] and create a type
constructor @racket[ty-ctor-id] for its instances.
Unless @racket[#:omit-accs] is specified, defines the accessor functions for the
struct.
}
@section{Builtin Data Structures}
@deftogether[(@defstruct[observe ([claim any?]) #:omit-constructor]
@defform[(Observe type)])]{
Constructs an assertion of interest.
}
@deftogether[(@defstruct[inbound ([assertion any?]) #:omit-constructor]
@defform[(Inbound type)])]{
Constructor for an assertion inbound from an outer dataspace.
}
@deftogether[(@defstruct[outbound ([assertion any?]) #:omit-constructor]
@defform[(Outbound type)])]{
Constructor for an assertion outbound to an outer dataspace.
}
@deftogether[(@defstruct[message ([body any?]) #:omit-constructor]
@defform[(Message type)])]{
Constructor for a broadcast message.
}
@subsection{Lists}
@defform[(List type)]{
The type for @racket[cons] lists whose elements are of type @racket[type].
}
@deftogether[(
@defthing[empty (List ⊥)]
@defthing[empty? (∀ (X) (→fn (List X) Bool))]
@defthing[cons (∀ (X) (→fn X (List X) (List X)))]
@defthing[cons? (∀ (X) (→fn X (List X) Bool))]
@defthing[first (∀ (X) (→fn (List X) X))]
@defthing[second (∀ (X) (→fn (List X) X))]
@defthing[rest (∀ (X) (→fn (List X) (List X)))]
@defthing[member? (∀ (X) (→fn X (List X) Bool))]
@defthing[reverse (∀ (X) (→fn (List X) (List X)))]
@defthing[partition (∀ (X) (→fn (List X) (→fn X Bool) (List X)))]
@defthing[map (∀ (X Y) (→fn (→fn X Y) (List X) (List Y)))]
@defthing[argmax (∀ (X) (→fn (→fn X Int) (List X) X))]
@defthing[argmin (∀ (X) (→fn (→fn X Int) (List X) X))]
@defthing[remove (∀ (X) (→fn X (List X) (List X)))]
@defthing[length (∀ (X) (→fn (List X) Int))]
@defform[(list e ...)]
)]{
Like their Racket counterparts.
}
@subsection{Sets}
@defform[(Set type)]{
The type for sets whose elements are of type @racket[type].
}
@deftogether[(
@defform[(set e ...)]
@defform[(set-union st ...+)]
@defform[(set-intersect st ...+)]
@defform[(set-subtract st ...+)]
@defthing[set-first (∀ (X) (→fn (Set X) X))]
@defthing[set-empty? (∀ (X) (→fn (Set X) Bool))]
@defthing[set-count (∀ (X) (→fn (Set X) Int))]
@defthing[set-add (∀ (X) (→fn (Set X) X (Set X)))]
@defthing[set-remove (∀ (X) (→fn (Set X) X (Set X)))]
@defthing[set-member? (∀ (X) (→fn (Set X) X Bool))]
@defthing[list->set (∀ (X) (→fn (List X) (Set X)))]
@defthing[set->list (∀ (X) (→fn (Set X) (List X)))]
)]{
Like their Racket counterparts.
}
@subsection{Hashes}
@defform[(Hash key-type value-type)]{
The type for key/value hash tables.
}
@deftogether[(
@defform[(hash key val ... ...)]
@defthing[hash-set (∀ (K V) (→fn (Hash K V) K V (Hash K V)))]
@defthing[hash-ref (∀ (K V) (→fn (Hash K V) K V))]
@defthing[hash-ref/failure (∀ (K V) (→fn (Hash K V) K V V))]
@defthing[hash-empty? (∀ (K V) (→fn (Hash K V) Bool))]
@defthing[hash-has-key? (∀ (K V) (→fn (Hash K V) K Bool))]
@defthing[hash-count (∀ (K V) (→fn (Hash K V) Int))]
@defthing[hash-update (∀ (K V) (→fn (Hash K V) K (→fn V V) (Hash K V)))]
@defthing[hash-update/failure (∀ (K V) (→fn (Hash K V) K (→fn V V) V (Hash K V)))]
@defthing[hash-remove (∀ (K V) (→fn (Hash K V) K (Hash K V)))]
@defthing[hash-map (∀ (K V R) (→fn (Hash K V) (→fn K V R) (List R)))]
@defthing[hash-keys (∀ (K V) (→fn (Hash K V) (List K)))]
@defthing[hash-values (∀ (K V) (→fn (Hash K V) (List V)))]
@defthing[hash-union (∀ (K1 V1 K2 V2) (→fn (Hash K1 V1) (Hash K2 V2) (Hash (U K1 K2) (U V1 V2))))]
@defthing[hash-union/combine (∀ (K V) (→fn (Hash K V) (Hash K V) (→fn V V V) (Hash K V)))]
@defthing[hash-keys-subset? (∀ (K1 V1 K2 V2) (→fn (Hash K1 V1) (Hash K2 V2) Bool))]
)]{
Like their Racket counterparts. The /failure and /combine suffixes are in place
of keyword arguments, which Typed Syndicate does not presently support.
}
@subsection{Sequences}
@defform[(Sequence type)]{
The type for a sequence of @racket[type] values.
}
@deftogether[(
@defthing[empty-sequence (Sequence ⊥)]
@defthing[sequence->list (∀ (X) (→fn (Sequence X) (List X)))]
@defthing[sequence-length (∀ (X) (→fn (Sequence X) Int))]
@defthing[sequence-ref (∀ (X) (→fn (Sequence X) Int Int))]
@defthing[sequence-tail (∀ (X) (→fn (Sequence X) Int (Sequence X)))]
@defthing[sequence-append (∀ (X) (→fn (Sequence X) (Sequence X) (Sequence X)))]
@defthing[sequence-map (∀ (A B) (→fn (→fn A B) (Sequence A) (Sequence B)))]
@defthing[sequence-andmap (∀ (X) (→fn (→fn X Bool) (Sequence X) Bool))]
@defthing[sequence-ormap (∀ (X) (→fn (→fn X Bool) (Sequence X) Bool))]
@defthing[sequence-fold (∀ (A B) (→fn (→fn A B A) (Sequence B) A))]
@defthing[sequence-count (∀ (X) (→fn (→fn X Bool) (Sequence X) Int))]
@defthing[sequence-filter (∀ (X) (→fn (→fn X Bool) (Sequence X) (Sequence X)))]
@defthing[sequence-add-between (∀ (X) (→fn (Sequence X) X (Sequence X)))]
@defthing[in-list (∀ (X) (→fn (List X) (Sequence X)))]
@defthing[in-hash-keys (∀ (K V) (→fn (Hash K V) (Sequence K)))]
@defthing[in-hash-values (∀ (K V) (→fn (Hash K V) (Sequence V)))]
@defthing[in-range (→fn Int (Sequence Int))]
@defthing[in-set (∀ (X) (→fn (Set X) (Sequence X)))]
)]{
Like their Racket counterparts.
}
@subsection{Maybe}
@deftogether[(
@defidform[None]
@defthing[none None]
@defstruct[some ([v any?]) #:omit-constructor]
@defform[(Some type)]
@defform[(Maybe type)]
)]{
@racket[(Maybe type)] is an alias for @racket[(U None (Some type))].
}
@subsection{Either}
@deftogether[(
@defstruct[left ([v any?]) #:omit-constructor]
@defform[(Left type)]
@defstruct[right ([v any?]) #:omit-constructor]
@defform[(Right type)]
@defform[(Either left-type right-type)]
)]{
@racket[(Either left-type right-type)] is an alias for @racket[(U (Left
left-type) (Right right-type))].
}
@defthing[partition/either (∀ (X Y Z) (→fn (List X) (→fn X (Either Y Z)) (Tuple (List Y) (List Z))))]{
Partition a list based on a function that returns an @racket[Either] value.
}
@section{Behavioral Checking}

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,8 @@
error
define-tuple
match-define
mk-tuple
tuple-select
(for-syntax (all-defined-out)))
(require "core-types.rkt")
@ -164,12 +166,15 @@
( ν-s #,(make-Branch #'((ss ...) ...)))])
;; (Listof Value) -> Value
(define- (mk-tuple es)
(#%app- cons- 'tuple es))
(define-typed-syntax (tuple e:expr ...)
[ e e- ( : τ)] ...
#:fail-unless (stx-andmap pure? #'(e- ...)) "expressions not allowed to have effects"
-----------------------
[ (#%app- list- 'tuple e- ...) ( : (Tuple τ ...))])
[ (#%app- mk-tuple (#%app- list- e- ...)) ( : (Tuple τ ...))])
(define unit : Unit (tuple))
@ -205,8 +210,6 @@
[(tuple p ...)
#:with (([x:id τ:type] ...) ...) (stx-map pat-bindings #'(p ...))
#'([x τ] ... ...)]
#;[(k:kons1 p)
(pat-bindings #'p)]
[(~constructor-exp cons p ...)
#:with (([x:id τ:type] ...) ...) (stx-map pat-bindings #'(p ...))
#'([x τ] ... ...)]
@ -260,9 +263,6 @@
[(tuple p ...)
(quasisyntax/loc pat
(tuple #,@(stx-map elaborate-pattern #'(p ...))))]
[(k:kons1 p)
(quasisyntax/loc pat
(k #,(elaborate-pattern #'p)))]
[(~constructor-exp ctor p ...)
(quasisyntax/loc pat
(ctor #,@(stx-map elaborate-pattern #'(p ...))))]
@ -277,10 +277,14 @@
[x:dollar-ann-id
(syntax/loc pat (bind x.id x.ty))]
[x:dollar-id
(when (bot? ty)
(raise-syntax-error #f "unable to instantiate pattern with matching part of type" pat))
(quasisyntax/loc pat (bind x.id #,ty))]
[($ x:id ty)
(syntax/loc pat (bind x ty))]
[($ x:id)
(when (bot? ty)
(raise-syntax-error #f "unable to instantiate pattern with matching part of type" pat))
(quasisyntax/loc pat (bind x #,ty))]
[(tuple p ...)
(define (matching? t)
@ -319,6 +323,7 @@
(define (proj t i)
(syntax-parse t
[(~constructor-type _ tt ...)
#:when (matching? t)
(if (= i -1)
t
(stx-list-ref #'(tt ...) i))]
@ -345,8 +350,6 @@
#:datum-literals (tuple discard bind)
[(tuple p ...)
#`(#,list-binding 'tuple #,@(stx-map loop #'(p ...)))]
#;[(k:kons1 p)
#`(#,(kons1->constructor #'k) #,(loop #'p))]
[(bind x:id τ:type)
(bind-id-transformer #'x)]
[discard

View File

@ -0,0 +1,22 @@
#lang typed/syndicate/roles
(provide activate!
later-than
LaterThanT
LaterThan
TimeStateDriver)
(require-struct later-than
#:as LaterThanT
#:from syndicate/drivers/timestate)
(define-type-alias LaterThan (LaterThanT Int))
(define-type-alias TimeStateDriver
(U LaterThan
(Observe (LaterThanT ★/t))))
;; TODO ignoring other driver underneath it
(require/typed (submod syndicate/drivers/timestate syndicate-main)
[activate! : (proc #:spawns ((Actor TimeStateDriver)))])

View File

@ -22,13 +22,14 @@
(define ( (X Y Z) (partition/either [xs : (List X)]
[pred : (→fn X (Either Y Z))]
-> (Tuple (List Y) (List Z))))
(for/fold ([acc (Tuple (List Y) (List Z)) (tuple (list) (list))])
(for/fold ([lefts (List Y) (list)]
[rights (List Z) (list)])
([x xs])
(define y-or-z (pred x))
(match y-or-z
[(left (bind y Y))
(tuple (cons y (select 0 acc))
(select 1 acc))]
(tuple (cons y lefts)
rights)]
[(right (bind z Z))
(tuple (select 0 acc)
(cons z (select 1 acc)))])))
(tuple lefts
(cons z rights))])))

View File

@ -13,7 +13,7 @@
(require (only-in "set.rkt" Set ~Set))
(require (only-in "hash.rkt" Hash ~Hash))
(require (only-in "prim.rkt" Bool + #%datum))
(require (only-in "core-expressions.rkt" let unit))
(require (only-in "core-expressions.rkt" let unit tuple-select mk-tuple))
(require "maybe.rkt")
(require (postfix-in - (only-in racket/set
@ -125,36 +125,68 @@
#,body))]))
(define-typed-syntax for/fold
[(for/fold ([acc:id (~optional (~datum :)) τ-acc init])
[(for/fold ([acc:id (~optional (~datum :)) τ-acc init] ...+)
(clause:iter-clause
...)
e-body ...+)
[ init init- ( : τ-acc)]
#:fail-unless (pure? #'init-) "expression must be pure"
[ init init- ( : τ-acc)] ...
#:fail-unless (all-pure? #'(init- ...)) "expression must be pure"
#:with (clauses- ([x x- τ] ...)) (analyze-for-clauses #'(clause.parend ...))
[[x x-- : τ] ...
[acc acc- : τ-acc] (block e-body ...) e-body-
( : τ-acc)
( ν-ep (~effs τ-ep ...))
( ν-s (~effs τ-s ...))
( ν-f (~effs τ-f ...))]
#:do [(define num-accs (length (syntax->list #'(τ-acc ...))))]
#:with body-ty (if (= 1 num-accs)
(first (syntax->list #'(τ-acc ...)))
(type-eval #'(Tuple (~@ τ-acc ...))))
[[[x x-- : τ] ...]
[[acc acc- : τ-acc] ...] (block e-body ...) e-body-
( : body-ty)
( ν-ep (~effs τ-ep ...))
( ν-s (~effs τ-s ...))
( ν-f (~effs τ-f ...))]
-------------------------------------------------------
[ (for/fold- ([acc- init-])
clauses-
#,(bind-renames #'([x-- x-] ...) #'e-body-))
( : τ-acc)
[ (values->tuple #,num-accs
(for/fold- ([acc- init-] ...)
clauses-
#,(bind-renames #'([x-- x-] ...) #`(tuple->values #,num-accs e-body-))))
( : body-ty)
( ν-ep (τ-ep ...))
( ν-s (τ-s ...))
( ν-f (τ-f ...))]]
[(for/fold ([acc:id init])
[(for/fold (accs ... [acc:id init] more-accs ...)
clauses
e-body ...+)
[ init _ ( : τ-acc)]
---------------------------------------------------
[ (for/fold ([acc τ-acc init])
[ (for/fold (accs ... [acc τ-acc init] more-accs ...)
clauses
e-body ...)]])
(define-syntax-parser tuple->values
[(_ n:nat e:expr)
(define arity (syntax-e #'n))
(cond
[(= 1 arity)
#'e]
[else
(define/with-syntax tmp (generate-temporary 'tup))
(define projections
(for/list ([i (in-range arity)])
#`(#%app- tuple-select #,i tmp)))
#`(let- ([tmp e])
(#%app- values- #,@projections))])])
#;(tuple->values 1 (tuple 0))
(define-syntax-parser values->tuple
[(_ n:nat e:expr)
(define arity (syntax-e #'n))
(cond
[(= 1 arity)
#'e]
[else
(define/with-syntax (tmp ...) (generate-temporaries (make-list arity 'values->tuple)))
#`(let-values- ([(tmp ...) e])
(#%app- mk-tuple (#%app- list- tmp ...)))])])
(define-typed-syntax (for/list (clause:iter-clause ...)
e-body ...+)
#:with (clauses- ([x x- τ] ...)) (analyze-for-clauses #'(clause.parend ...))

View File

@ -33,32 +33,20 @@
(define-container-type Hash #:arity = 2)
(begin-for-syntax
(define-splicing-syntax-class key-val-list
#:attributes (items)
(pattern (~seq k1 v1 rest:key-val-list)
#:attr items #`((k1 v1) #,@#'rest.items))
(pattern (~seq)
#:attr items #'())))
(define-typed-syntax (hash keys&vals:key-val-list)
#:with ((key val) ...) #'keys&vals.items
(define-typed-syntax (hash (~seq key:expr val:expr) ...)
[ key key- ( : τ-k)] ...
[ val val- ( : τ-val)] ...
#:fail-unless (all-pure? #'(key- ... val- ...)) "gotta be pure"
#:with together-again (stx-flatten #'((key- val-) ...))
--------------------------------------------------
[ (#%app- hash- #,@#'together-again) ( : (Hash (U τ-k ...) (U τ-val ...)))])
[ (#%app- hash- (~@ key val) ...) ( : (Hash (U τ-k ...) (U τ-val ...)))])
(require/typed racket/base
;; don't have a type for ConsPair
#;[make-hash : ( (K V) (→fn (List (ConsPair K V)) (Hash K V)))]
[hash-set : ( (K V) (→fn (Hash K V) K V (Hash K V)))]
[hash-ref : ( (K V) (→fn (Hash K V) K V))]
;; TODO hash-ref/failure
[hash-has-key? : ( (K V) (→fn (Hash K V) K Bool))]
[hash-update : ( (K V) (→fn (Hash K V) K (→fn V V) (Hash K V)))]
;; TODO hash-update/failure
[hash-remove : ( (K V) (→fn (Hash K V) K (Hash K V)))]
[hash-map : ( (K V R) (→fn (Hash K V) (→fn K V R) (List R)))]
[hash-keys : ( (K V) (→fn (Hash K V) (List K)))]
@ -71,7 +59,6 @@
(require/typed racket/hash
[hash-union : ( (K1 V1 K2 V2) (→fn (Hash K1 V1) (Hash K2 V2) (Hash (U K1 K2) (U V1 V2))))]
;; TODO - hash-union with #:combine
)
(define- (hash-ref/failure- h k err)

View File

@ -1,2 +1,2 @@
#lang s-exp syntax/module-reader
typed/main
typed/syndicate/roles

View File

@ -3,18 +3,24 @@
(provide List
(for-syntax ~List)
list
(typed-out [[cons- : ( (X) (→fn X (List X) (List X)))] cons]
(typed-out [[empty- : (List )] empty]
[[empty?- : ( (X) (→fn (List X) Bool))] empty?]
[[cons- : ( (X) (→fn X (List X) (List X)))] cons]
[[cons?- : ( (X) (→fn X (List X) Bool))] cons?]
[[first- : ( (X) (→fn (List X) X))] first]
[[second- : ( (X) (→fn (List X) X))] second]
[[rest- : ( (X) (→fn (List X) (List X)))] rest]
[[member?- ( (X) (→fn X (List X) Bool))] member?]
[[empty?- ( (X) (→fn (List X) Bool))] empty?]
[[reverse- ( (X) (→fn (List X) (List X)))] reverse]
[[partition- ( (X) (→fn (List X) (→fn X Bool) (List X)))] partition]
[[map- ( (X Y) (→fn (→fn X Y) (List X) (List Y)))] map]))
[[map- ( (X Y) (→fn (→fn X Y) (List X) (List Y)))] map]
[[argmax- : ( (X) (→fn (→fn X Int) (List X) X))] argmax]
[[argmin- : ( (X) (→fn (→fn X Int) (List X) X))] argmin]
[[remove- : ( (X) (→fn X (List X) (List X)))] remove]
[[length- : ( (X) (→fn (List X) Int))] length]))
(require "core-types.rkt")
(require (only-in "prim.rkt" Bool))
(require (only-in "prim.rkt" Bool Int))
(require (postfix-in - racket/list))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -15,15 +15,15 @@
;; - Bool
;; where X represents the type of atomic propositions
(struct always [p] #:transparent)
(struct eventually [p] #:transparent)
(struct atomic [p] #:transparent)
(struct weak-until [p q] #:transparent)
(struct strong-until [p q] #:transparent)
(struct ltl-implies [p q] #:transparent)
(struct ltl-and [p q] #:transparent)
(struct ltl-or [p q] #:transparent)
(struct ltl-not [p] #:transparent)
(struct always [p] #:prefab)
(struct eventually [p] #:prefab)
(struct atomic [p] #:prefab)
(struct weak-until [p q] #:prefab)
(struct strong-until [p q] #:prefab)
(struct ltl-implies [p q] #:prefab)
(struct ltl-and [p q] #:prefab)
(struct ltl-or [p q] #:prefab)
(struct ltl-not [p] #:prefab)
;; [LTL X] {X -> Y} -> [LTL Y]
(define (map-atomic ltl op)

View File

@ -32,6 +32,8 @@
(define-primop zero? (→fn Int Bool))
(define-primop positive? (→fn Int Bool))
(define-primop negative? (→fn Int Bool))
(define-primop current-inexact-milliseconds (→fn Int))
(define-primop string=? (→fn String String Bool))
(define-primop bytes->string/utf-8 ( ByteString (Computation (Value String) (Endpoints) (Roles) (Spawns))))
(define-primop string->bytes/utf-8 ( String (Computation (Value ByteString) (Endpoints) (Roles) (Spawns))))

View File

@ -20,26 +20,26 @@
;; - (Sends τ)
;; - (Realizes τ)
;; - (Stop FacetName Body)
(struct Role (nm eps) #:transparent)
(struct Spawn (ty) #:transparent)
(struct Sends (ty) #:transparent)
(struct Realizes (ty) #:transparent)
(struct Stop (nm body) #:transparent)
(struct Role (nm eps) #:prefab)
(struct Spawn (ty) #:prefab)
(struct Sends (ty) #:prefab)
(struct Realizes (ty) #:prefab)
(struct Stop (nm body) #:prefab)
;; a EP is one of
;; - (Reacts D Body), describing an event handler
;; - (Shares τ), describing an assertion
;; - (Know τ), describing an internal assertion
(struct Reacts (evt body) #:transparent)
(struct Shares (ty) #:transparent)
(struct Know (ty) #:transparent)
(struct Reacts (evt body) #:prefab)
(struct Shares (ty) #:prefab)
(struct Know (ty) #:prefab)
;; a Body describes actions carried out in response to some event, and
;; is one of
;; - T
;; - (Listof Body)
;; - (Branch (Listof Body))
(struct Branch (arms) #:transparent)
(struct Branch (arms) #:prefab)
;; a D is one of
;; - (Asserted τ), reaction to assertion
@ -51,11 +51,11 @@
;; - StartEvt, reaction to facet startup
;; - StopEvt, reaction to facet shutdown
;; - DataflowEvt, reaction to field updates
(struct Asserted (ty) #:transparent)
(struct Retracted (ty) #:transparent)
(struct Message (ty) #:transparent)
(struct Forget (ty) #:transparent)
(struct Realize (ty) #:transparent)
(struct Asserted (ty) #:prefab)
(struct Retracted (ty) #:prefab)
(struct Message (ty) #:prefab)
(struct Forget (ty) #:prefab)
(struct Realize (ty) #:prefab)
(define StartEvt 'Start)
(define StopEvt 'Stop)
(define DataflowEvt 'Dataflow)
@ -68,8 +68,8 @@
;; specified facet,
;; - (StartOf FacetName)
;; - (StopOf FacetName)
(struct StartOf (fn) #:transparent)
(struct StopOf (fn) #:transparent)
(struct StartOf (fn) #:prefab)
(struct StopOf (fn) #:prefab)
;; NOTE: because I'm adding D+ after writing a bunch of code using only D,
;; expect inconsistencies in signatures and names
@ -84,17 +84,17 @@
;; - ⋆
;; - (Base Symbol)
;; - (internal-label Symbol τ)
(struct U (tys) #:transparent)
(struct Struct (nm tys) #:transparent)
(struct Observe (ty) #:transparent)
(struct List (ty) #:transparent)
(struct Set (ty) #:transparent)
(struct Hash (ty-k ty-v) #:transparent)
(struct Mk⋆ () #:transparent)
(struct internal-label (actor-id ty) #:transparent)
(struct U (tys) #:prefab)
(struct Struct (nm tys) #:prefab)
(struct Observe (ty) #:prefab)
(struct List (ty) #:prefab)
(struct Set (ty) #:prefab)
(struct Hash (ty-k ty-v) #:prefab)
(struct Mk⋆ () #:prefab)
(struct internal-label (actor-id ty) #:prefab)
;; TODO this might be a problem when used as a match pattern
(define (Mk⋆))
(struct Base (name) #:transparent)
(struct Base (name) #:prefab)
(define Int (Base 'Int))
(define String (Base 'String))
(define Bool (Base 'Bool))
@ -237,24 +237,25 @@
(match-define (role-graph st0 st#) rg)
(check-true (hash-has-key? st# (set 'x 'y)))))
;; a DetectedCylce is a (List (Listof StateName) D D D), as in
;; (list path init evt D)
;; where
;; - path represents the sequences of states containing a cycle,
;; - init is the external event that initiated this activity
;; - evt is the last-taken internal event
;; - D is the edge in the graph that matched evt
;; a DetectedCylce is a (detected-cycle StateName (Listof TraversalStep)), as in
;; (detected-cycle start steps)
;; where path represents the sequences of states containing a cycle,
(struct detected-cycle (start steps) #:transparent)
;; a TraversalStep is a (traversal-step D StateName)
;; representing a state transition along an edge matching D to a destination state
(struct traversal-step (evt dest) #:transparent)
;; RoleGraph Role -> (U RoleGraph DetectedCycle)
;; "Optimize" the given role graph with respect to internal events.
;; The resulting graph will have transitions of only external events.
(define (compile/internal-events rg role)
(define (compile/internal-events rg)
(match-define (role-graph st0 orig-st#) rg)
;; doing funny business with state (set) here
(define orig-st#+ (hash-set orig-st# (set) (state (set) (hash) (set))))
;; a WorkItem is a
;; (work-item StateName (Listof StateName) D+ (Listof D+) (Listof TransitionEffect))
;; (work-item TraversalStep (Listof TraversalStep) D+ (Listof D+) (Listof TransitionEffect))
;; such as (work-item from path/r to by with effs), where
;; - from is the origin state for this chain of events
;; - path/r is the list of states in the path to this point, *after* from, in reverse
@ -264,6 +265,7 @@
;; - with is a list of pending events to be processed
;; - effs are the external effects emitted on this path
(struct work-item (from path/r to by with effs) #:transparent)
(let/ec fail
(define (walk work visited st#)
(match work
@ -290,20 +292,21 @@
(set! states (hash-set states (set) (state (set) (hash) (set)))))
(role-graph new-st0 states)]
[(cons (work-item from path/r to by with effs) more-work)
(define prev (if (empty? path/r) from (first path/r)))
(match-define (traversal-step last-evt cur-st) to)
(define prev (if (empty? path/r) from (traversal-step-dest (first path/r))))
(define prev-assertions (state-assertions (hash-ref orig-st#+ prev)))
(match-define (state _ txn# cur-assertions) (hash-ref orig-st#+ to))
(match-define (state _ txn# cur-assertions) (hash-ref orig-st#+ cur-st))
(define new-state-changes (route-internal prev-assertions
cur-assertions))
(define state-changes* (for/list ([D (in-set new-state-changes)]
#:when (for/or ([D/actual (in-hash-keys txn#)])
(D<:? D D/actual)))
D))
(define started (for*/list ([fn (in-set (set-subtract to prev))]
(define started (for*/list ([fn (in-set (set-subtract cur-st prev))]
[D (in-value (StartOf fn))]
#:when (hash-has-key? txn# D))
D))
(define stopped (for*/list ([fn (in-set (set-subtract prev to))]
(define stopped (for*/list ([fn (in-set (set-subtract prev cur-st))]
[D (in-value (StopOf fn))]
#:when (hash-has-key? txn# D))
D))
@ -332,18 +335,12 @@
#:when (implies (DataflowEvt? D) (DataflowEvt? evt))
[t (in-set ts)])
(match-define (transition more-effs dest) t)
(when (and (member dest path/r+)
;; TODO - cycles involving Start/Stop are tricky. Punt for now
(not (start/stop-evt? evt)))
(fail (list (cons from (reverse (cons dest path/r+)))
by
evt
D)))
(check-for-cycle! from path/r+ evt dest fail)
(define-values (internal-effs external-effs)
(partition-transition-effects more-effs))
(work-item from
path/r+
dest
(traversal-step evt dest)
by
(append more-pending internal-effs)
(append effs external-effs)))]))
@ -361,9 +358,9 @@
(cond
[(ormap empty? induced-work)
;; this is the end of some path
(define visited+ (set-add visited to))
(define visited+ (set-add visited cur-st))
(define new-paths-work
(for*/list (#:unless (set-member? visited to)
(for*/list (#:unless (set-member? visited cur-st)
[(D txns) (in-hash txn#)]
#:when (external-evt? D)
#:unless (equal? D DataflowEvt)
@ -371,21 +368,43 @@
(match-define (transition es dst) t)
(define-values (internal-effs external-effs)
(partition-transition-effects es))
(work-item to '() dst D internal-effs external-effs)))
(define new-st# (update-path st# from to by effs))
(work-item cur-st '() (traversal-step D dst) D internal-effs external-effs)))
(define new-st# (update-path st# from cur-st by effs))
(walk (append more-work induced-work* new-paths-work) visited+ new-st#)]
[else
(walk (append more-work induced-work*) visited st#)])]))
(walk (list (work-item (set) '() st0 StartEvt '() '()))
(walk (list (work-item (set) '() (traversal-step StartEvt st0) StartEvt '() '()))
(set)
(hash))))
;; (Listof TraceStep) D StateName (DetectedCycle -> X) -> (U X Void)
;; the path is in reverse, and the final step is the pair evt/dest;
;; so their is a cycle if the sequence from the first occurrence of
;; dest in the path matches the sequence from the second occurrence to
;; the first.
(define (check-for-cycle! from path/r evt dest fail)
;; TraceStep -> Bool
(define (same-state? step) (equal? dest (traversal-step-dest step)))
;; (Listof TraceStep) -> (Values (Listof TraceStep) (Listof TraceStep))
(define (split-at-same-state steps) (splitf-at steps (compose not same-state?)))
(define-values (loop1 trail) (split-at-same-state path/r))
(when (cons? trail)
(match-define (cons prior-last trail2) trail)
(define-values (loop2 trail3) (split-at-same-state trail2))
(define last-step (traversal-step evt dest))
(when (and (cons? trail3)
(equal? (cons last-step loop1)
(cons prior-last loop2)))
(fail (detected-cycle from (reverse (cons last-step path/r)))))))
(module+ test
(test-case
"most minimal functionality for removing internal events"
;; manager role has basically nothing to it
(define m (compile manager))
(define i (compile/internal-events m manager))
(define i (compile/internal-events m))
(check-true (role-graph? i))
(check-true (simulates?/rg i m))
(check-true (simulates?/rg m i))
@ -396,7 +415,7 @@
;; because it doesn't use any internal events, it should be unaffected
(define tmr (parse-T task-runner-ty))
(define tm (compile tmr))
(define tmi (compile/internal-events tm tmr))
(define tmi (compile/internal-events tm))
(check-true (role-graph? tmi))
;; I'm not exactly sure how the two should be related via simulation :S
(check-true (simulates?/rg tmi tm)))
@ -410,15 +429,15 @@
(Realizes Int))))
(define r (parse-T cyclic))
(define rg (compile r))
(define i (run/timeout (thunk (compile/internal-events rg r))))
(check-true (list? i))
(check-equal? (length i) 4)
(match-define (list path kick-off evt edge) i)
;; the first 'x -> 'x cycle is ignored because it's a Start event
(check-equal? path (list (set) (set 'x) (set 'x) (set 'x)))
(check-equal? kick-off StartEvt)
(check-match evt (Realize (internal-label _ (== Int))))
(check-match edge (Realize (internal-label _ (== Int)))))
(define i (run/timeout (thunk (compile/internal-events rg))))
(check-true (detected-cycle? i))
(check-match i
(detected-cycle
(== (set))
(list (traversal-step 'Start (== (set 'x)))
(traversal-step (StartOf 'x) (== (set 'x)))
(traversal-step (Realize (internal-label _ (== Int))) (== (set 'x)))
(traversal-step (Realize (internal-label _ (== Int))) (== (set 'x)))))))
(test-case
"interesting internal start event"
(test-case
@ -433,7 +452,7 @@
(define r (parse-T strt))
(define rg (run/timeout (thunk (compile r))))
(check-true (role-graph? rg))
(define rgi (run/timeout (thunk (compile/internal-events rg r))))
(define rgi (run/timeout (thunk (compile/internal-events rg))))
(check-true (role-graph? rgi))
(match-define (role-graph st0 st#) rgi)
(check-equal? st0 (set 'x 'y))
@ -454,7 +473,7 @@
(Role 'y (list)))))))
(define rg (run/timeout (thunk (compile role))))
(check-true (role-graph? rg))
(define rgi (run/timeout (thunk (compile/internal-events rg role))))
(define rgi (run/timeout (thunk (compile/internal-events rg))))
(check-true (role-graph? rgi))
(define state# (role-graph-states rgi))
(check-true (hash-has-key? state# (set 'x)))
@ -471,7 +490,7 @@
(define role (run/timeout (thunk (parse-T desc))))
(define rg (run/timeout (thunk (compile role))))
(check-true (role-graph? rg))
(define rgi (run/timeout (thunk (compile/internal-events rg role))))
(define rgi (run/timeout (thunk (compile/internal-events rg))))
(check-true (role-graph? rgi))
(check-match rgi
(role-graph (== (set 'x 'y))
@ -646,9 +665,9 @@
(check-equal? sn0 (set 'seller))
(check-true (hash-has-key? seller# (set 'seller)))
(check-true (hash-has-key? seller# (set 'seller 'fulfill)))
(check-equal? (hash-keys seller#)
(list (set 'seller 'fulfill)
(set 'seller)))
(check-equal? (list->set (hash-keys seller#))
(set (set 'seller 'fulfill)
(set 'seller)))
(define st0 (hash-ref seller# (set 'seller)))
(define transitions (state-transitions st0))
(define quote-request
@ -802,12 +821,12 @@
(define seller+spawn (Role 'start (list (Reacts StartEvt (Spawn seller)))))
(define rg (run/timeout (thunk (compile seller+spawn))))
(check-true (role-graph? rg))
(define rgi (compile/internal-events rg seller+spawn))
(define rgi (compile/internal-events rg))
(check-true (role-graph? rgi))
(define srg (compile seller))
(check-true (run/timeout (thunk (simulates?/rg rg rg))))
(check-false (run/timeout (thunk (simulates?/rg rg srg))))
(check-false (run/timeout (thunk (simulates?/rg srg rg))))
(check-true (run/timeout (thunk (simulates?/rg srg rg))))
(check-true (run/timeout (thunk (simulates?/rg rgi srg))))
(check-true (run/timeout (thunk (simulates?/rg srg rgi)))))
(test-case
@ -822,7 +841,7 @@
(Role 'know (list))))))))))
(define rg (run/timeout (thunk (compile role))))
(check-true (role-graph? rg))
(define rgi (run/timeout (thunk (compile/internal-events rg role))))
(define rgi (run/timeout (thunk (compile/internal-events rg))))
(check-true (role-graph? rgi))
(define state-names (hash-keys (role-graph-states rgi)))
(for ([sn (in-list state-names)])
@ -979,19 +998,24 @@
(for/fold ([st st])
([c (in-list children)])
(set-remove st c)))
(for/fold ([txns (set (transition '() st-))])
([f-name (in-list children)])
(define stop-effs (hash-ref (hash-ref txn# f-name) (StopOf f-name)))
(define stop-effs+ (if (set-empty? stop-effs)
(set '())
stop-effs))
(for*/set ([txn (in-set txns)]
[st (in-value (transition-dest txn))]
[effs* (in-set stop-effs+)]
[next-txn (in-set (loop st (append effs* rest)))])
(transition (append (transition-effs txn)
(transition-effs next-txn))
(transition-dest next-txn))))])])))
(define-values (final-txns _)
(for/fold ([txns (set (transition '() st-))]
[pending-effs rest])
([f-name (in-list children)])
(define stop-effs (hash-ref (hash-ref txn# f-name) (StopOf f-name)))
(define stop-effs+ (if (set-empty? stop-effs)
(set '())
stop-effs))
(define new-txns
(for*/set ([txn (in-set txns)]
[st (in-value (transition-dest txn))]
[effs* (in-set stop-effs+)]
[next-txn (in-set (loop st (append pending-effs effs*)))])
(transition (append (transition-effs txn)
(transition-effs next-txn))
(transition-dest next-txn))))
(values new-txns '())))
final-txns])])))
(module+ test
(test-case
@ -1001,7 +1025,61 @@
(set)
(facet-tree (hash) (hash))
(hash)))
(check-equal? txns (set (transition (list (realize Int) (realize String)) (set))))))
(check-equal? txns (set (transition (list (realize Int) (realize String)) (set)))))
(test-case
"another bug in apply-effects"
;; was duplicating some effects
(define r #s(Role
run-a-round342
(#s(Shares
#s(Struct
RoundT
(#s(Base Symbol) #s(Base String) #s(List #s(Base String)))))
#s(Reacts
Start
#s(Role
wait364
(#s(Reacts
#s(Asserted #s(Struct LaterThanT (#s(Base Int))))
#s(Branch
((#s(Branch
((#s(Stop
run-a-round342
(#s(Role
over356
(#s(Shares
#s(Struct
ElectedT
(#s(Base String)
#s(Base String)))))))))
(#s(Stop
run-a-round342
(#s(Realizes
#s(Struct
StartRoundT
(#s(Set #s(Base String))
#s(Set #s(Base String)))))))))))
())))))))))
(define labeled-role (label-internal-events r))
(define roles# (describe-roles labeled-role))
(define ft (make-facet-tree r))
(define current (set 'wait364 'run-a-round342))
(define eff* (list
(stop 'run-a-round342)
(realize
'#s(internal-label
initial31336
#s(Struct
StartRoundT
(#s(Set #s(Base String)) #s(Set #s(Base String))))))))
(check-equal? (apply-effects eff* current ft roles#)
(set (transition
(list
(realize
'#s(internal-label
initial31336
#s(Struct StartRoundT (#s(Set #s(Base String)) #s(Set #s(Base String)))))))
(set))))))
;; FacetTree FacetName (Setof FacetName) -> (List FacetName)
;; return the facets in names that are children of the given facet nm, ordered
@ -1518,6 +1596,7 @@
(verify g (set-add assumptions goal)))
(unless (same-on-specified-events? transitions1
transitions2
sn1
verify/with-current-assumed)
(return #f))
(return (same-on-extra-events? transitions1
@ -1544,6 +1623,178 @@
))])))
(verify (equiv st0-1 st0-2) (set)))
;; Role Role -> Bool
(define (simulates?/report-error impl spec)
(define impl-rg (compile/internal-events (compile impl)))
(define spec-rg (compile/internal-events (compile spec)))
(cond
[(detected-cycle? impl-rg)
(printf "Detected Cycle in Implementation!\n")
(describe-detected-cycle impl-rg)
#f]
[(detected-cycle? spec-rg)
(printf "Detected Cycle in Specification!\n")
(describe-detected-cycle spec-rg)
#f]
[(simulates?/rg impl-rg spec-rg)
#t]
[else
(define trace (find-simulation-counterexample impl-rg spec-rg))
(print-failing-trace trace impl-rg spec-rg)
#f]))
;; DetectedCycle -> Void
(define (describe-detected-cycle dc)
(printf "Initial State: ~a\n" (detected-cycle-start dc))
(for ([step (in-list (detected-cycle-steps dc))])
(printf " :: ~a ==> ~a\n" (D->label (traversal-step-evt step)) (traversal-step-dest step))))
;; a FailingTrace is a (failing-trace (Listof Transition) (Listof Transition) (Listof TraceStep))
(struct failing-trace (impl-path spec-path steps) #:transparent)
;; a TraceStep is one of
;; - (both-step D)
;; - (impl-step D)
;; - (spec-step D)
;; describing either both the spec and the implementation responding to an
;; event, only the implementation, or only the spec
(struct both-step (evt) #:transparent)
(struct impl-step (evt) #:transparent)
(struct spec-step (evt) #:transparent)
;; FailingTrace RoleGraph RoleGraph -> Void
(define (print-failing-trace trace impl-rg spec-rg)
(match-define (role-graph _ impl-st#) impl-rg)
(match-define (role-graph _ spec-st#) spec-rg)
(match-define (failing-trace impl-path spec-path steps) trace)
(define SEP (make-string 40 #\;))
(define (print-sep)
(newline)
(displayln SEP)
(newline))
(let loop ([steps steps]
[impl-path impl-path]
[spec-path spec-path]
;; because the path might end with an impl-step or spec-step, remember the last
;; states we've seen so we can print its assertions at the right time
[last-spec-state (transition-dest (car spec-path))]
[last-impl-state (transition-dest (car impl-path))])
(define (get-spec-dest)
(transition-dest (car spec-path)))
(define (get-impl-dest)
(transition-dest (car impl-path)))
(match steps
[(cons step more-steps)
(print-sep)
(printf "In response to event:\n")
(match step
[(or (both-step D)
(impl-step D)
(spec-step D))
(pretty-print D)])
(when (or (both-step? step) (impl-step? step))
(define impl-effs (transition-effs (car impl-path)))
(printf "Implementation steps to state:\n")
(pretty-print (get-impl-dest))
(unless (empty? impl-effs)
(printf "With Effects:\n")
(pretty-print impl-effs)))
(when (empty? more-steps)
(define impl-final (if (spec-step? step) last-impl-state (get-impl-dest)))
(printf "Implementation Assertions:\n")
(pretty-print (state-assertions (hash-ref impl-st# impl-final))))
(when (or (both-step? step) (spec-step? step))
(define spec-effs (transition-effs (car spec-path)))
(printf "Specification steps to state:\n")
(pretty-print (get-spec-dest))
(unless (empty? spec-effs)
(printf "With Effects:\n")
(pretty-print spec-effs)))
(when (empty? more-steps)
(define spec-final (if (impl-step? step) last-spec-state (get-spec-dest)))
(printf "Specification Assertions:\n")
(pretty-print (state-assertions (hash-ref spec-st# spec-final))))
(loop more-steps
(if (spec-step? step) impl-path (cdr impl-path))
(if (impl-step? step) spec-path (cdr spec-path))
(if (impl-step? step) last-spec-state (get-spec-dest))
(if (spec-step? step) last-impl-state (get-impl-dest)))]
[_
(newline)
(void)])))
;; RoleGraph RoleGraph -> Trace
;; assuming impl-rg does not simulate spec-rg, find a trace of transitions
;; (event + effects + destination assertions) demonstrating different behaviors
(define (find-simulation-counterexample impl-rg spec-rg)
(match-define (role-graph impl-st0 impl-st#) impl-rg)
(match-define (role-graph spec-st0 spec-st#) spec-rg)
;; inside loop, the each trace field is in reverse
(let loop ([work (list (failing-trace (list (transition '() impl-st0))
(list (transition '() spec-st0))
(list (both-step StartEvt))))]
#;[visited (set)])
(match work
[(cons (failing-trace impl-path/rev spec-path/rev steps/rev) more-work)
(match-define (transition impl-effs impl-dest) (car impl-path/rev))
(match-define (transition spec-effs spec-dest) (car spec-path/rev))
(define last-step (car steps/rev))
(cond
[(or (impl-step? last-step)
;; when only the implementation steps, no need to compare effects on transitions
(and (spec-step? last-step) (empty? spec-effs))
(effects-subsequence? spec-effs impl-effs))
;; cascading conds will help with development and isolating where things go wrong
(match-define (state _ impl-transition# impl-assertions) (hash-ref impl-st# impl-dest))
(match-define (state _ spec-transition# spec-assertions) (hash-ref spec-st# spec-dest))
(cond
;; n.b. internal events should be compiled away by now or this wouldn't work
[(assertion-superset? impl-assertions spec-assertions)
;; same effects and same assertions, compare transitions
;; TODO: similarity to `same-on-specified-events?`
(define spec-matching-txns
(for*/list ([(spec-D spec-txns) (in-hash spec-transition#)]
[(impl-D impl-txns) (in-hash impl-transition#)]
#:when (D<:? spec-D impl-D)
[spec-txn (in-set spec-txns)]
[impl-txn (in-set impl-txns)])
(failing-trace (cons impl-txn impl-path/rev)
(cons spec-txn spec-path/rev)
(cons (both-step spec-D) steps/rev))))
;; transitions that the spec has but the implementation doesn't respond to
;; TODO: similarity to `same-on-extra-events?`
(define impl-evts (hash-keys impl-transition#))
(define spec-extra-txns
(for*/list ([(spec-D spec-txns) (in-hash spec-transition#)]
;; TODO - this more or less assumes that *any* event matching impl-D also matches spec-evt, which I'm not sure is quite right
#:unless (for/or ([impl-evt (in-list impl-evts)])
(D<:? impl-evt spec-D))
[spec-txn (in-set spec-txns)])
(failing-trace impl-path/rev
(cons spec-txn spec-path/rev)
(cons (spec-step spec-D) steps/rev))))
;; TODO: similarity to above code
;; transitions that the implementation has that the spec doesn't respond to
(define spec-evts (hash-keys spec-transition#))
(define impl-extra-txns
(for*/list ([(impl-D impl-txns) (in-hash impl-transition#)]
;; TODO - this more or less assumes that *any* event matching impl-D also matches spec-evt, which I'm not sure is quite right
#:unless (for/or ([spec-evt (in-list spec-evts)])
(D<:? spec-evt impl-D))
[impl-txn (in-set impl-txns)])
(failing-trace (cons impl-txn impl-path/rev)
spec-path/rev
(cons (impl-step impl-D) steps/rev))))
(loop (append more-work spec-matching-txns spec-extra-txns impl-extra-txns))]
[else
;; states have different assertions
(failing-trace (reverse impl-path/rev) (reverse spec-path/rev) (reverse steps/rev))])]
[else
;; transitions have different effects
(failing-trace (reverse impl-path/rev) (reverse spec-path/rev) (reverse steps/rev))])]
[_
(error "ran out of work")])))
;; (List Role) -> (Hashof RoleName (Setof τ))
;; map each role's name to the assertions it contributes
(define (all-roles-assertions roles)
@ -1571,7 +1822,11 @@
;; as determined by the verify procedure
;; and the effects on the edge going to Y are a supersequence of the effects
;; on the edge to Y
(define (same-on-specified-events? transitions1 transitions2 verify)
;; and:
;; Determine if the events in transitions2 that don't have any match in transitions1, are:
;; - all effect free
;; - verify with sn1 matched to each destination
(define (same-on-specified-events? transitions1 transitions2 sn1 verify)
(for/and ([(D2 edges2) (in-hash transitions2)])
(define edges1
(for/fold ([agg (set)])
@ -1585,7 +1840,11 @@
(set-union agg txns1)))
(cond
[(set-empty? edges1)
#f]
;; - I think this is right, as long as the current state of the implementation
;; matches all states the spec steps to --- unless there are effects on the transition
(for/and ([txn (in-set edges2)])
(and (empty? (transition-effs txn))
(verify (equiv sn1 (transition-dest txn)))))]
[else
(define combos (make-combinations edges1 edges2))
(verify (one-of combos))])))
@ -1694,19 +1953,69 @@
(define (simulating-subgraphs impl spec)
;; assume spec doesn't have any internal events
(define spec-rg (compile spec))
(define impl-rg (compile/internal-events (compile impl) impl))
(define impl-rg (compile/internal-events (compile impl)))
(define evts (relevant-events spec-rg))
(for/list ([srg (subgraphs impl-rg evts)]
#:when (simulates?/rg srg spec-rg))
srg))
;; Role Role -> (Maybe RoleGraph)
;; try to find any subgraph of the implementation simulating the spec
;; TODO: would be nice to find the largest
(define (find-simulating-subgraph impl spec)
(define spec-rg (compile spec))
(define impl-rg (compile/internal-events (compile impl)))
(find-simulating-subgraph/rg impl-rg spec-rg))
;; RoleGraph RoleGraph -> (Maybe RoleGraph)
(define (find-simulating-subgraph/rg impl-rg spec-rg)
(define evts (relevant-events spec-rg))
(for/first ([srg (subgraphs impl-rg evts)]
#:when (simulates?/rg srg spec-rg))
srg))
;; Role Role -> Bool
(define (find-simulating-subgraph/report-error impl spec)
(define spec-rg (compile spec))
(define impl-rg (compile/internal-events (compile impl)))
(define ans (find-simulating-subgraph/rg impl-rg spec-rg))
(cond
[ans
#t]
[else
(define-values (ft sg) (find-largest-simulating-subgraph-counterexample impl-rg spec-rg))
(print-failing-trace ft impl-rg spec-rg)
#f]))
;; RoleGraph RoleGraph -> (Values FailingTrace RoleGraph)
;; assuming impl does not have any simulating subgraphs of spec
;; largest *trace*, not largest subgraph
(define (find-largest-simulating-subgraph-counterexample impl-rg spec-rg)
(define evts (relevant-events spec-rg))
(define-values (trace len rg)
(for/fold ([best-trace #f]
[best-length 0]
[best-subgraph #f])
([srg (subgraphs impl-rg evts)])
(define ft (find-simulation-counterexample srg spec-rg))
(define len (failing-trace-length ft))
;; thing >= will prefer larger graphs
(if (>= len best-length)
(values ft len srg)
(values best-trace best-length best-subgraph))))
(values trace rg))
;; FailingTrace -> Int
(define (failing-trace-length ft)
(length (failing-trace-steps ft)))
(module+ test
(test-case
"task manager has task performer subgraphs"
(define tpr (parse-T task-performer-spec))
(define tmr (parse-T task-manager-ty))
(define ans (simulating-subgraphs tmr tpr))
(check-equal? (length ans) 68)
(check-equal? (length ans) 340)
(define tprg (compile tpr))
(check-true (simulates?/rg (first ans) tprg))
(check-true (simulates?/rg (second ans) tprg))))
@ -1746,15 +2055,16 @@
(define st#
(for/hash ([st (in-list states*)])
(match-define (state _ orig-txn# assertions) (hash-ref state# st))
(define (enabled-txns D)
(define orig-txns (hash-ref orig-txn# D))
(for/set ([txn (in-set orig-txns)]
#:when (set-member? states (transition-dest txn)))
txn))
(define txn#
(for/hash ([D (in-hash-keys orig-txn#)]
#:when (event-enabled? D))
(define orig-txns (hash-ref orig-txn# D))
(define new-txns
(for/set ([txn (in-set orig-txns)]
#:when (set-member? states (transition-dest txn)))
txn))
;; TODO - what if new-txns is empty?
(for*/hash ([D (in-hash-keys orig-txn#)]
#:when (event-enabled? D)
[new-txns (in-value (enabled-txns D))]
#:unless (set-empty? new-txns))
(values D new-txns)))
(values st (state st txn# assertions))))
(for ([st0 (in-list states*)])
@ -2577,10 +2887,12 @@
"job manager reads and compiles"
(define jmr (run/timeout (thunk (parse-T job-manager-actual))))
(check-true (Role? jmr))
(define jm (run/timeout (thunk (compile jmr))))
(define jm (run/timeout (thunk (compile jmr)) 5000))
(check-true (role-graph? jm))
(define jmi (run/timeout (thunk (compile/internal-events jm jmr))))
(check-true (run/timeout (thunk (simulates?/rg jmi jmi))))))
(define jmi (run/timeout (thunk (compile/internal-events jm)) 5000))
(check-true (role-graph? jmi))
;; TODO : times out, probably due to infinite loop
#;(check-true (run/timeout (thunk (simulates?/rg jmi jmi)) 100000))))
(define task-runner-ty
'(Role
@ -2715,12 +3027,12 @@
(check-false (simulates? tm (parse-T task-performer-spec)))))
(module+ test
#;(module+ test
(test-case
"job manager subgraph(s) implement task assigner"
(define jmr (run/timeout (thunk (parse-T job-manager-actual))))
(define tar (parse-T task-assigner-spec))
(define ans (run/timeout (thunk (simulating-subgraphs jmr tar)) 1800))
(define ans (run/timeout (thunk (simulating-subgraphs jmr tar)) 4000))
(check-true (list? ans))
(check-false (empty? ans))))
@ -2990,7 +3302,40 @@
(Stop y))))))
(define r (parse-T ty))
(define rg (compile r))
(define rgi (compile/internal-events rg r))
(define rgi (compile/internal-events rg))
(render-to-file rg "before.dot")
(render-to-file rgi "after.dot")
)
(module+ test
(test-case
"regression: ok for implementation not to have edges if the current state matches"
(define a (role-graph
(set 'seller341 'during-inner343)
(hash
(set 'seller341 'during-inner343)
(state
(set 'seller341 'during-inner343)
'#hash()
(set
'#s(Observe #s(Observe #s(Struct BookQuoteT (#s(Base String) #s(Mk⋆)))))
'#s(Struct BookQuoteT (#s(Base String) #s(Base Int))))))))
(define b (role-graph
(set 'seller)
(hash
(set 'seller)
(state
(set 'seller)
(hash
'#s(Asserted #s(Observe #s(Struct BookQuoteT (#s(Base String) #s(Mk⋆)))))
(set (transition '() (set '_ 'seller))))
(set
'#s(Observe #s(Observe #s(Struct BookQuoteT (#s(Base String) #s(Mk⋆)))))))
(set '_ 'seller)
(state
(set '_ 'seller)
'#hash()
(set
'#s(Observe #s(Observe #s(Struct BookQuoteT (#s(Base String) #s(Mk⋆)))))
'#s(Struct BookQuoteT (#s(Base String) #s(Base Int))))))))
(check-true (run/timeout (thunk (simulates?/rg a b))))))

View File

@ -0,0 +1,3 @@
#!/bin/sh
spin -p -t $1

View File

@ -4,21 +4,25 @@
#%app
(rename-out [typed-quote quote])
#%top-interaction
require only-in
module+ module*
;; require & provides
require only-in prefix-in except-in rename-in
provide all-defined-out all-from-out rename-out except-out
for-syntax for-template for-label for-meta struct-out
;; Start dataspace programs
run-ground-dataspace
;; Types
Tuple Bind Discard
Tuple Bind Discard AssertionSet
Role Reacts Shares Asserted Retracted Message OnDataflow Stop OnStart OnStop
Know Forget Realize
Branch Effs
FacetName Field ★/t
Observe Inbound Outbound Actor U
Computation Value Endpoints Roles Spawns
Computation Value Endpoints Roles Spawns Sends
→fn proc
;; Statements
let let* if spawn dataspace start-facet set! begin stop begin/dataflow #;unsafe-do
when unless send! realize! define
let let* if spawn dataspace start-facet set! begin block stop begin/dataflow #;unsafe-do
when unless send! realize! define during/spawn
;; Derived Forms
during During
define/query-value
@ -29,7 +33,7 @@
;; endpoints
assert know on field
;; expressions
tuple select lambda ref observe inbound outbound
tuple select lambda λ ref (struct-out observe) (struct-out message) (struct-out inbound) (struct-out outbound)
Λ inst call/inst
;; making types
define-type-alias
@ -43,7 +47,7 @@
;; primitives
(all-from-out "prim.rkt")
;; expressions
(all-from-out "core-expressions.rkt")
(except-out (all-from-out "core-expressions.rkt") mk-tuple tuple-select)
;; lists
(all-from-out "list.rkt")
;; sets
@ -59,10 +63,13 @@
(all-from-out "either.rkt")
;; DEBUG and utilities
print-type print-role role-strings
;; Behavioral Roles
export-roles export-type check-simulates check-has-simulating-subgraph lift+define-role
verify-actors
;; LTL Syntax
True False Always Eventually Until WeakUntil Implies And Or Not A M
;; Extensions
match cond
;; require & provides
require provide
submod for-syntax for-meta only-in except-in
require/typed
require-struct
@ -80,13 +87,21 @@
(require (prefix-in syndicate: syndicate/actor-lang))
(require (submod syndicate/actor priorities))
(require (prefix-in syndicate: (submod syndicate/actor for-module-begin)))
(require (for-meta 2 macrotypes/stx-utils racket/list syntax/stx syntax/parse racket/base))
(require macrotypes/postfix-in)
(require (for-syntax turnstile/mode))
(require turnstile/typedefs)
(require (postfix-in - racket/list))
(require (postfix-in - racket/set))
(require (for-syntax (prefix-in proto: "proto.rkt")
(prefix-in proto: "ltl.rkt")
syntax/id-table)
(prefix-in proto: "proto.rkt")
(prefix-in proto: "compile-spin.rkt"))
(module+ test
(require rackunit)
(require rackunit/turnstile))
@ -122,7 +137,20 @@
;; Core forms
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-typed-syntax (start-facet name:id ep ...+)
(define-typed-syntax start-facet
[(_ name:id #:implements ~! spec:type ep ...+)
[ (start-facet name ep ...) e- ( ν-f (~effs impl-ty))]
#:fail-unless (simulating-types? #'impl-ty #'spec.norm)
"facet does not implement specification"
------------------------------------------------------------
[ e-]]
[(_ name:id #:includes-behavior ~! spec:type ep ...+)
[ (start-facet name ep ...) e- ( ν-f (~effs impl-ty))]
#:fail-unless (type-has-simulating-subgraphs? #'impl-ty #'spec.norm)
"no subset implements specified behavior"
------------------------------------------------------------
[ e-]]
[(_ name:id ep ...+)
#:with name- (syntax-local-identifier-as-binding (syntax-local-introduce (generate-temporary #'name)))
#:with name+ (syntax-local-identifier-as-binding #'name)
#:with facet-name-ty (type-eval #'FacetName)
@ -139,7 +167,7 @@
(~and τ-k (~Know _))
;; untyped syndicate might allow this - TODO
#;(~and τ-m (~Sends _))
(~and τ-r (~Reacts _ ...))
(~and τ-r (~Reacts _ _ ...))
~MakesField)
...)
ep-effects
@ -152,23 +180,51 @@
[ (syndicate:react (let- ([#,name-- (#%app- syndicate:current-facet-id)])
#,@ep-...))
( : ★/t)
( ν-f (τ))])
( ν-f (τ))]])
(define-typed-syntax (field [x:id τ-f:type e:expr] ...)
#:fail-unless (stx-andmap flat-type? #'(τ-f ...)) "keep your uppity data outta my fields"
[ e e- ( : τ-f)] ...
#:fail-unless (stx-andmap pure? #'(e- ...)) "field initializers not allowed to have effects"
#:with (x- ...) (generate-temporaries #'(x ...))
#:with (τ ...) (stx-map type-eval #'((Field τ-f.norm) ...))
#:with MF (type-eval #'MakesField)
----------------------------------------------------------------------
[ (erased (field/intermediate [x x- τ e-] ...))
(define-typed-syntax (during/spawn pat bdy ...+)
#:with pat+ (elaborate-pattern/with-com-ty #'pat)
[ pat+ pat-- ( : τp)]
#:fail-unless (pure? #'pat--) "pattern not allowed to have effects"
#:fail-unless (allowed-interest? (pattern-sub-type #'τp)) "overly broad interest, ?̱̱★ and ??★ not allowed"
#:with ([x:id τ:type] ...) (pat-bindings #'pat+)
[[x x- : τ] ... (block bdy ...) bdy-
( ν-ep (~effs τ-ep ...))
( ν-f (~effs))
( ν-s (~effs))]
#:with pat- (substs #'(x- ...) #'(x ...) (compile-syndicate-pattern #'pat+))
#:with τc:type (current-communication-type)
#:with τ-facet (type-eval #'(Role (_) τ-ep ...))
#:with τ-spawn (mk-ActorWithRole- #'(τc.norm τ-facet))
#:with τ-endpoint (type-eval #'(Reacts (Asserted τp) τ-spawn))
------------------------------
[ (syndicate:during/spawn pat- bdy-)
( : ★/t)
( ν-ep (MF))])
( ν-ep (τ-endpoint))])
(define-typed-syntax field
[(_ [x:id (~optional (~datum :)) τ-f:type e:expr] ...)
#:cut
#:fail-unless (stx-andmap flat-type? #'(τ-f ...)) "keep your uppity data outta my fields"
[ e e- ( : τ-f)] ...
#:fail-unless (stx-andmap pure? #'(e- ...)) "field initializers not allowed to have effects"
#:with (x- ...) (generate-temporaries #'(x ...))
#:with (τ ...) (stx-map type-eval #'((Field τ-f.norm) ...))
#:with MF (type-eval #'MakesField)
----------------------------------------------------------------------
[ (erased (field/intermediate [x x- τ e-] ...))
( : ★/t)
( ν-ep (MF))]]
[(_ flds ... [x:id e:expr] more-flds ...)
#:cut
[ e e- ( : τ)]
--------------------
[ (field flds ... [x τ e-] more-flds ...)]])
(define-typed-syntax (assert e:expr)
[ e e- ( : τ)]
#:fail-unless (pure? #'e-) "expression not allowed to have effects"
#:fail-unless (allowed-interest? #'τ) "overly broad interest, ?̱̱★ and ??★ not allowed"
#:with τs (mk-Shares- #'(τ))
-------------------------------------
[ (syndicate:assert e-) ( : ★/t)
@ -204,7 +260,7 @@
( ν-ep (~effs))
( ν-s (~effs))
( ν-f (~effs τ-f ...))]
#:with τ (mk-Stop- #`(facet-name- τ-f ...))
#:with τ #'(Stop facet-name- τ-f ...)
---------------------------------------------------------------------------------
[ (syndicate:stop-facet facet-name- cont-) ( : ★/t)
( ν-f (τ))])
@ -280,6 +336,7 @@
#:with p/e (if msg? (stx-cadr elab) elab)
[ p/e p-- ( : τp)]
#:fail-unless (pure? #'p--) "pattern not allowed to have effects"
#:fail-unless (allowed-interest? (pattern-sub-type #'τp)) "overly broad interest, ?̱̱★ and ??★ not allowed"
#:with ([x:id τ:type] ...) (pat-bindings #'p/e)
[[x x- : τ] ... (block s ...) s-
( ν-ep (~effs))
@ -322,10 +379,13 @@
[ (block s) s- ( ν-ep (~effs)) ( ν-s (~effs)) ( ν-f (~effs τ-f ...))]
]
;; TODO: s shouldn't refer to facets or fields!
#:fail-unless (and (stx-andmap Role? #'(τ-f ...))
(= 1 (length (syntax->list #'(τ-f ...)))))
"expected exactly one Role for body"
#:with (τ-i τ-o τ-i/i τ-o/i τ-a) (analyze-roles #'(τ-f ...))
#:fail-unless (<: #'τ-o #'τ-c.norm)
(format "Output ~a not valid in dataspace ~a" (type->str #'τ-o) (type->str #'τ-c.norm))
#:with τ-final (mk-Actor- #'(τ-c.norm))
#:with τ-final #;(mk-Actor- #'(τ-c.norm)) (mk-ActorWithRole- #'(τ-c.norm τ-f ...))
#:fail-unless (<: #'τ-a #'τ-final)
"Spawned actors not valid in dataspace"
#:fail-unless (project-safe? ( (strip-? #'τ-o) #'τ-c.norm)
@ -397,11 +457,6 @@
#:with inst-p (instantiate-pattern #'p+)
#:with start-e (if (attribute k) #'know #'asserted)
#:with stop-e (if (attribute k) #'forget #'retracted)
#:with res #'(on (start-e p+)
(start-facet during-inner
(on (stop-e inst-p)
(stop during-inner))
s ...))
----------------------------------------
[ (on (start-e p+)
(start-facet during-inner
@ -427,8 +482,6 @@
#:datum-literals (tuple discard bind)
[(tuple p ...)
#`(tuple #,@(stx-map loop #'(p ...)))]
[(k:kons1 p)
#`(k #,(loop #'p))]
[(bind x:id τ)
#'x]
;; not sure about this
@ -450,7 +503,7 @@
#'τ]
[(~U* τ ...)
(mk-U- (stx-map instantiate-pattern-type #'(τ ...)))]
[(~Any/bvs τ-cons () τ ...)
[(~Any/new τ-cons τ ...)
#:when (reassemblable? #'τ-cons)
(define subitems (for/list ([t (in-syntax #'(τ ...))])
(instantiate-pattern-type t)))
@ -576,8 +629,10 @@
[ s s- ( : t1)] ...
[ (dataspace τ-c.norm s- ...) _ ( : t2)]
]
#:with τ-out (strip-outbound #'τ-c.norm)
-----------------------------------------------------------------------------------
[ (#%app- syndicate:run-ground s- ...) ( : (AssertionSet τ-c))])
[ (#%app- syndicate:run-ground (#%app- syndicate:capture-actor-actions (lambda- () (#%app- list- s- ...))))
( : (AssertionSet τ-out))])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Utilities
@ -604,11 +659,218 @@
----------------------------------------
[ (#%app- list- (#%datum- . s) ...) ( : (List String))])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; LTL Syntax
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-type LTL : LTL)
(define-type True : LTL)
(define-type False : LTL)
(define-type Always : LTL -> LTL)
(define-type Eventually : LTL -> LTL)
(define-type Until : LTL LTL -> LTL)
(define-type WeakUntil : LTL LTL -> LTL)
(define-type Implies : LTL LTL -> LTL)
(define-type And : LTL * -> LTL)
(define-type Or : LTL * -> LTL)
(define-type Not : LTL -> LTL)
(define-type A : Type -> LTL) ;; Assertions
(define-type M : Type -> LTL) ;; Messages
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Behavioral Analysis
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(begin-for-syntax
(define ID-PHASE 0)
(define-syntax (build-id-table stx)
(syntax-parse stx
[(_ (~seq key val) ...)
#'(make-free-id-table (hash (~@ #'key val) ...) #:phase ID-PHASE)]))
(define (mk-proto:U . args)
(proto:U args))
(define (mk-proto:Branch . args)
(proto:Branch args))
(define TRANSLATION#
(build-id-table Spawns proto:Spawn
Sends proto:Sends
Realizes proto:Realizes
Shares proto:Shares
Know proto:Know
Branch mk-proto:Branch
Effs list
Asserted proto:Asserted
Retracted proto:Retracted
Message proto:Message
Forget proto:Forget
Realize proto:Realize
U* mk-proto:U
Observe proto:Observe
List proto:List
Set proto:Set
Hash proto:Hash
OnStart proto:StartEvt
OnStop proto:StopEvt
OnDataflow proto:DataflowEvt
;; LTL
True #t
False #f
Always proto:always
Eventually proto:eventually
Until proto:strong-until
WeakUntil proto:weak-until
Implies proto:ltl-implies
And proto:&&
Or proto:||
Not proto:ltl-not
A proto:atomic
M (compose proto:atomic proto:Message)))
(define (double-check)
(for/first ([id (in-dict-keys TRANSLATION#)]
#:when (false? (identifier-binding id)))
(pretty-print id)
(pretty-print (syntax-debug-info id))))
(define (synd->proto ty)
(let convert ([ty (resugar-type ty)])
(syntax-parse ty
#:literals (★/t Bind Discard ∀/internal →/internal Role/internal Stop Reacts Actor ActorWithRole)
[(ctor:id t ...)
#:when (dict-has-key? TRANSLATION# #'ctor)
(apply (dict-ref TRANSLATION# #'ctor) (stx-map convert #'(t ...)))]
[nm:id
#:when (dict-has-key? TRANSLATION# #'nm)
(dict-ref TRANSLATION# #'nm)]
[(Actor _)
(error "only able to convert actors with roles")]
[(ActorWithRole _ r)
(proto:Spawn (convert #'r))]
[★/t proto:⋆]
[(Bind t)
;; TODO - this is debatable handling
(convert #'t)]
[Discard
;; TODO - should prob have a Discard type in proto
proto:⋆]
[(∀/internal (X ...) body)
;; TODO
(error "unimplemented")]
[(→/internal ty-in ... ty-out)
;; TODO
(error "unimplemented")]
[(Role/internal (nm) body ...)
(proto:Role (syntax-e #'nm) (stx-map convert #'(body ...)))]
[(Stop nm body ...)
(proto:Stop (syntax-e #'nm) (stx-map convert #'(body ...)))]
[(Reacts evt body ...)
(define converted-body (stx-map convert #'(body ...)))
(define body+
(if (= 1 (length converted-body))
(first converted-body)
converted-body))
(proto:Reacts (convert #'evt) body+)]
[t:id
(proto:Base (syntax-e #'t))]
[(ctor:id args ...)
;; assume it's a struct
(proto:Struct (syntax-e #'ctor) (stx-map convert #'(args ...)))]
[unrecognized (error (format "unrecognized type: ~a" #'unrecognized))]))))
(define-typed-syntax (export-roles dest:string e:expr)
[ e e- ( : τ) ( ν-ep (~effs eps ...)) ( ν-f (~effs fs ...)) ( ν-s (~effs ss ...))]
#:do [(with-output-to-file (syntax-e #'dest)
(thunk (for ([f (in-syntax #'(fs ...))])
(pretty-write (synd->proto f))))
#:exists 'replace)]
----------------------------------------
[ e- ( : τ) ( ν-ep (eps ...)) ( ν-f (fs ...)) ( ν-s (ss ...))])
(define-typed-syntax (export-type dest:string τ:type)
#:do [(with-output-to-file (syntax-e #'dest)
(thunk (pretty-write (synd->proto #'τ.norm)))
#:exists 'replace)]
----------------------------------------
[ (#%app- void-) ( : ★/t)])
(define-typed-syntax (lift+define-role x:id e:expr)
[ e e- ( : τ) ( ν-ep (~effs)) ( ν-f ((~and r (~Role (_) _ ...)))) ( ν-s (~effs))]
;; because turnstile introduces a lot of intdef scopes; ideally, we'd be able to synthesize somethign
;; with the right module scopes
#:with x+ (syntax-local-introduce (datum->syntax #f (syntax-e #'x)))
#:do [(define r- (synd->proto #'r))
(syntax-local-lift-module-end-declaration #`(define- x+ '#,r-))]
----------------------------------------
[ e- ( : τ) ( ν-ep ()) ( ν-f (r)) ( ν-s ())])
;; Type Type -> Bool
;; (normalized Types)
(define-for-syntax (simulating-types? ty-impl ty-spec)
(define ty-impl- (synd->proto ty-impl))
(define ty-spec- (synd->proto ty-spec))
(proto:simulates?/report-error ty-impl- ty-spec-))
;; Type Type -> Bool
;; (normalized Types)
(define-for-syntax (type-has-simulating-subgraphs? ty-impl ty-spec)
(define ty-impl- (synd->proto ty-impl))
(define ty-spec- (synd->proto ty-spec))
(define ans (proto:find-simulating-subgraph/report-error ty-impl- ty-spec-))
(unless ans
(pretty-print ty-impl-)
(pretty-print ty-spec-))
ans)
(define- (ensure-Role! r)
(unless- (#%app- proto:Role? r)
(#%app- error- 'check-simulates "expected a Role type, got " r))
r)
(begin-for-syntax
(define-syntax-class type-or-proto
#:attributes (role)
(pattern t:type #:attr role #`(quote- #,(synd->proto #'t.norm)))
(pattern x:id #:attr role #'(#%app- ensure-Role! x))
#;(pattern ((~literal quote-) r)
#:do [(define r- (syntax-e ))]
#:when (proto:Role? r-)
#:attr role r-)))
(require rackunit)
(define-syntax-parser check-simulates
[(_ τ-impl:type-or-proto τ-spec:type-or-proto)
(syntax/loc this-syntax
(check-true (#%app- proto:simulates?/report-error τ-impl.role τ-spec.role)))])
(define-syntax-parser check-has-simulating-subgraph
[(_ τ-impl:type-or-proto τ-spec:type-or-proto)
(syntax/loc this-syntax
(check-not-false (#%app- proto:find-simulating-subgraph/report-error τ-impl.role τ-spec.role)))])
(define-syntax-parser verify-actors
[(_ spec actor-ty:type-or-proto ...)
#:with spec- #`(quote- #,(synd->proto (type-eval #'spec)))
(syntax/loc this-syntax
(check-true (#%app- proto:compile+verify spec- (#%app- list- actor-ty.role ...))))])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Tests
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module+ test
(check-type
(spawn (U (Observe (Tuple Int ★/t)))
(start-facet echo
(on (message (tuple 1 $x:Int))
#f)))
: ★/t)
(check-type (spawn (U (Message (Tuple String Int))
(Observe (Tuple String ★/t)))
(start-facet echo

View File

@ -1,2 +1,2 @@
#lang s-exp syntax/module-reader
typed/roles
typed/syndicate/roles

View File

@ -0,0 +1,12 @@
#!/bin/sh
pushd ${1%/*}/
EXE="$1-verifier.o"
spin -a $1
gcc -o $EXE pan.c
$EXE -a -f -n -N $2
rm $EXE pan.c
popd

View File

@ -17,12 +17,15 @@
sequence-add-between
in-list
in-set
in-hash-keys
in-hash-values
in-range
)
(require "core-types.rkt")
(require (only-in "list.rkt" List))
(require (only-in "set.rkt" Set))
(require (only-in "hash.rkt" Hash))
(require (only-in "prim.rkt" Int Bool))
#;(require (postfix-in - racket/sequence))
@ -50,25 +53,8 @@
(require/typed racket/base
[in-list : ( (X) (→fn (List X) (Sequence X)))]
[in-hash-keys : ( (K V) (→fn (Hash K V) (Sequence K)))]
[in-hash-values : ( (K V) (→fn (Hash K V) (Sequence V)))]
[in-range : (→fn Int (Sequence Int))])
(require/typed racket/set
[in-set : ( (X) (→fn (Set X) (Sequence X)))])
#;(define-typed-syntax empty-sequence
[_
--------------------
[ empty-sequence- ( : (Sequence (U)))]])
;; er, this is making everything a macro, as opposed to a procedure...
;; think I ought to add polymorphous first :\
#;(define-typed-syntax (sequence->list s)
[ s s- ( : (~Sequence τ))]
#:fail-unless (pure? #'s-)
------------------------------
[ (sequence->list- s-) ( : (List τ))])
#;(define-typed-syntax (sequence-length s)
[ s s- ( : (~Sequence τ))]
#:fail-unless (pure? #'s-)
------------------------------
[ (sequence-length- s-) ( : Int)])

View File

@ -3,23 +3,28 @@
(provide Set
(for-syntax ~Set)
set
set-member?
set-add
set-remove
set-count
;; set-member?
;; set-add
;; set-remove
;; set-count
set-union
set-subtract
set-intersect
list->set
set->list
(typed-out [[set-first- : ( (X) (→fn (Set X) X))]
set-first]
[[set-empty?- : ( (X) (→fn (Set X) Bool))]
set-empty?]))
;; list->set
;; set->list
(typed-out [[set-first- : ( (X) (→fn (Set X) X))] set-first]
[[set-empty?- : ( (X) (→fn (Set X) Bool))] set-empty?]
[[set-count- : ( (X) (→fn (Set X) Int))] set-count]
[[set-add- : ( (X) (→fn (Set X) X (Set X)))] set-add]
[[set-remove- : ( (X) (→fn (Set X) X (Set X)))] set-remove]
[[set-member?- : ( (X) (→fn (Set X) X Bool))] set-member?]
[[list->set- : ( (X) (→fn (List X) (Set X)))] list->set]
[[set->list- : ( (X) (→fn (Set X) (List X)))] set->list]
))
(require "core-types.rkt")
(require (only-in "prim.rkt" Int Bool))
(require (only-in "list.rkt" ~List))
(require (only-in "list.rkt" ~List List))
(require (postfix-in - racket/set))
@ -35,38 +40,6 @@
---------------
[ (#%app- set- e- ...) (Set (U τ ...))])
(define-typed-syntax (set-count e)
[ e e- (~Set _)]
#:fail-unless (pure? #'e-) "expression must be pure"
----------------------
[ (#%app- set-count- e-) Int])
(define-typed-syntax (set-add st v)
[ st st- (~Set τs)]
#:fail-unless (pure? #'st-) "expression must be pure"
[ v v- τv]
#:fail-unless (pure? #'v-) "expression must be pure"
-------------------------
[ (#%app- set-add- st- v-) (Set (U τs τv))])
(define-typed-syntax (set-remove st v)
[ st st- (~Set τs)]
#:fail-unless (pure? #'st-) "expression must be pure"
[ v v- τs]
#:fail-unless (pure? #'v-) "expression must be pure"
-------------------------
[ (#%app- set-remove- st- v-) (Set τs)])
(define-typed-syntax (set-member? st v)
[ st st- (~Set τs)]
#:fail-unless (pure? #'st-) "expression must be pure"
[ v v- τv]
#:fail-unless (pure? #'v-) "expression must be pure"
#:fail-unless (<: #'τv #'τs)
"type mismatch"
-------------------------------------
[ (#%app- set-member?- st- v-) Bool])
(define-typed-syntax (set-union st0 st ...)
[ st0 st0- (~Set τ-st0)]
#:fail-unless (pure? #'st0-) "expression must be pure"
@ -91,15 +64,3 @@
#:fail-unless (all-pure? #'(st- ...)) "expressions must be pure"
-------------------------------------
[ (#%app- set-subtract- st0- st- ...) (Set τ-st0)])
(define-typed-syntax (list->set l)
[ l l- (~List τ)]
#:fail-unless (pure? #'l-) "expression must be pure"
-----------------------
[ (#%app- list->set- l-) (Set τ)])
(define-typed-syntax (set->list s)
[ s s- (~Set τ)]
#:fail-unless (pure? #'s-) "expression must be pure"
-----------------------
[ (#%app- set->list- s-) (List τ)])

View File

@ -4,6 +4,7 @@
#define RETRACTED(x) (x##_assertions == 0)
#define ASSERT(x) x##_assertions = x##_assertions + 1
#define RETRACT(x) x##_assertions = x##_assertions - 1
#define SEND(x) x##_messages = x##_messages + 1
/* Rest of Program */

View File

@ -0,0 +1,302 @@
#lang racket/base
(provide serialize-syntax deserialize-syntax)
(require racket/dict racket/match)
(struct serialized-syntax (unique-tag table contents) #:prefab)
(struct stx-with-props (stx ps) #:prefab)
(struct syntax-val (stx) #:prefab)
(struct datum-val (d) #:prefab)
(struct ref (unique-tag sym) #:prefab)
;(require racket/pretty)
(define (serialize-syntax stx)
(define unique-tag (gensym))
(define table (hasheq))
(define dedup-table (hasheq))
(define (dedup k f)
(if (hash-has-key? dedup-table k)
(hash-ref dedup-table k)
(let ([res (f)])
(set! dedup-table (hash-set dedup-table k res))
res)))
(define (lift! el)
(define tag-sym (gensym))
(set! table (hash-set table tag-sym el))
(ref unique-tag tag-sym))
(define (build-props! orig-s d)
(stx-with-props
(datum->syntax orig-s d orig-s #f)
(for/list ([k (syntax-property-symbol-keys orig-s)]
#:when (syntax-property-preserved? orig-s k))
(define val (syntax-property orig-s k))
(define serialized-val
(if (syntax? val)
(syntax-val (serialize-element! val))
(datum-val (serialize-element! val #:always-lift? #t))))
(cons k serialized-val))))
(define (serialize-element! el #:always-lift? [always-lift? #f])
(dedup
el
(lambda ()
(syntax-map
el
(lambda (tail? d) d)
(lambda (orig-s d)
;(when (and always-lift? (not (ref? (hash-ref dedup-table orig-s)))) ; TODO
;(error 'dedup "lift error"))
(dedup
orig-s
(lambda ()
(if (or always-lift?
(ormap (lambda (p) (syntax-property-preserved? orig-s p))
(syntax-property-symbol-keys orig-s)))
(lift! (build-props! orig-s d))
(datum->syntax orig-s d orig-s #f)))))
syntax-e))))
(define top-s (serialize-element! stx))
(define res (datum->syntax #f (serialized-syntax unique-tag table top-s)))
res)
(define (deserialize-syntax ser)
(match (syntax-e ser)
[(serialized-syntax unique-tag-stx table-stx contents)
(define unique-tag (syntax-e unique-tag-stx))
(define table (syntax-e table-stx))
(define dedup-table (hasheq))
(define (dedup k f)
(if (hash-has-key? dedup-table k)
(hash-ref dedup-table k)
(let ([res (f)])
(set! dedup-table (hash-set dedup-table k res))
res)))
(define (maybe-syntax-e v)
(if (syntax? v) (syntax-e v) v))
(define (deserialize-stx-with-props ref-sym)
(match-define (stx-with-props stx ps) (syntax-e (hash-ref table ref-sym)))
(define deserialized-nested-stx (deserialize-element stx))
(for/fold ([stx deserialized-nested-stx])
([stx-pr (syntax->list ps)])
(define pr (syntax-e stx-pr))
(define k (syntax-e (car pr)))
(define v (syntax-e (cdr pr)))
(define prop-val
(match v
[(syntax-val v)
(deserialize-element v)]
[(datum-val v)
(deserialize-element (syntax->datum v))]))
(syntax-property stx k prop-val #t)))
(define (deserialize-element el)
(dedup
el
(lambda ()
(syntax-map
el
(lambda (tail? d)
(match d
[(ref tag sym)
#:when (equal? (maybe-syntax-e tag) unique-tag)
(dedup
sym
(lambda () (deserialize-stx-with-props (maybe-syntax-e sym))))]
[_ d]))
(lambda (orig-s d)
(dedup
orig-s
(lambda () (datum->syntax orig-s d orig-s #f))))
syntax-e))))
(define res (deserialize-element contents))
res]))
(module+ test
(require rackunit)
(define type
(syntax-property
(syntax-property #'Int ':: #'Type #t)
'orig (list #'Int) #t))
(define term (syntax-property #`(1 #,(syntax-property #'2 ': type #t)) ': #'Type #t))
(define s (serialize-syntax term))
(define d (deserialize-syntax s))
(check-true
(bound-identifier=?
(syntax-property d ':)
#'Type))
; syntax with properties inside outer syntax with properties.
(check-true
(bound-identifier=?
(syntax-property (syntax-property (cadr (syntax-e d)) ':) '::)
#'Type))
(check-true
(bound-identifier=?
(syntax-property (cadr (syntax-e d)) ':)
#'Int))
(check-equal?
(syntax-position term)
(syntax-position d))
(check-equal?
(syntax-position (syntax-property (cadr (syntax-e term)) ':))
(syntax-position (syntax-property (cadr (syntax-e d)) ':)))
(check-equal?
(syntax-position (car (syntax-e term)))
(syntax-position (car (syntax-e d))))
; syntax in datum in properties
(check-true
(bound-identifier=?
(car (syntax-property (syntax-property (cadr (syntax-e d)) ':) 'orig))
#'Int))
)
;; ----------------------------------------------------------------
;; syntax-map and datum-map copied from the expander files
;; syntax/datum-map.rkt
;; syntax/syntax.rkt
(require racket/fixnum racket/prefab)
;; `(datum-map v f)` walks over `v`, traversing objects that
;; `datum->syntax` traverses to convert content to syntax objects.
;;
;; `(f tail? d)` is called on each datum `d`, where `tail?`
;; indicates that the value is a pair/null in a `cdr` --- so that it
;; doesn't need to be wrapped for `datum->syntax`, for example;
;; the `tail?` argument is actually #f or a fixnum for a lower bound
;; on `cdr`s that have been taken
;;
;; `gf` is like `f`, but `gf` is used when the argument might be
;; syntax; if `gf` is provided, `f` can assume that its argument
;; is not syntax
;;
;; If a `seen` argument is provided, then it should be an `eq?`-based
;; hash table, and cycle checking is enabled; when a cycle is
;; discovered, the procedure attached to 'cycle-fail in the initial
;; table is called
;;
;; If a `known-pairs` argument is provided, then it should be an
;; `eq?`-based hash table to map pairs that can be returned as-is
;; in a `tail?` position
;; The inline version uses `f` only in an application position to
;; help avoid allocating a closure. It also covers only the most common
;; cases, defering to the general (not inlined) function for other cases.
(define (datum-map s f [gf f] [seen #f] [known-pairs #f])
(let loop ([tail? #f] [s s] [prev-depth 0])
(define depth (fx+ 1 prev-depth)) ; avoid cycle-checking overhead for shallow cases
(cond
[(and seen (depth . fx> . 32))
(datum-map-slow tail? s (lambda (tail? s) (gf tail? s)) seen known-pairs)]
[(null? s) (f tail? s)]
[(pair? s)
(f tail? (cons (loop #f (car s) depth)
(loop 1 (cdr s) depth)))]
[(symbol? s) (f #f s)]
[(boolean? s) (f #f s)]
[(number? s) (f #f s)]
[(or (vector? s) (box? s) (prefab-struct-key s) (hash? s))
(datum-map-slow tail? s (lambda (tail? s) (gf tail? s)) seen known-pairs)]
[else (gf #f s)])))
(define (datum-map-slow tail? s f seen known-pairs)
(let loop ([tail? tail?] [s s] [prev-seen seen])
(define seen
(cond
[(and prev-seen (datum-has-elements? s))
(cond
[(hash-ref prev-seen s #f)
((hash-ref prev-seen 'cycle-fail) s)]
[else (hash-set prev-seen s #t)])]
[else prev-seen]))
(cond
[(null? s) (f tail? s)]
[(pair? s)
(cond
[(and known-pairs
tail?
(hash-ref known-pairs s #f))
s]
[else
(f tail? (cons (loop #f (car s) seen)
(loop (if tail? (fx+ 1 tail?) 1) (cdr s) seen)))])]
[(or (symbol? s) (boolean? s) (number? s))
(f #f s)]
[(vector? s)
(f #f (vector->immutable-vector
(for/vector #:length (vector-length s) ([e (in-vector s)])
(loop #f e seen))))]
[(box? s)
(f #f (box-immutable (loop #f (unbox s) seen)))]
[(immutable-prefab-struct-key s)
=> (lambda (key)
(f #f
(apply make-prefab-struct
key
(for/list ([e (in-vector (struct->vector s) 1)])
(loop #f e seen)))))]
[(and (hash? s) (immutable? s))
(cond
[(hash-eq? s)
(f #f
(for/hasheq ([(k v) (in-hash s)])
(values k (loop #f v seen))))]
[(hash-eqv? s)
(f #f
(for/hasheqv ([(k v) (in-hash s)])
(values k (loop #f v seen))))]
[else
(f #f
(for/hash ([(k v) (in-hash s)])
(values k (loop #f v seen))))])]
[else (f #f s)])))
(define (datum-has-elements? d)
(or (pair? d)
(vector? d)
(box? d)
(immutable-prefab-struct-key d)
(and (hash? d) (immutable? d) (positive? (hash-count d)))))
;; `(syntax-map s f d->s)` walks over `s`:
;;
;; * `(f tail? d)` is called to each datum `d`, where `tail?`
;; indicates that the value is a pair/null in a `cdr` --- so that it
;; doesn't need to be wrapped for `datum->syntax`, for example
;;
;; * `(d->s orig-s d)` is called for each syntax object,
;; and the second argument is result of traversing its datum
;;
;; * the `s-e` function extracts content of a syntax object
;;
;; The optional `seen` argument is an `eq?`-based immutable hash table
;; to detect and reject cycles. See `datum-map`.
(define (syntax-map s f d->s s-e [seen #f])
(let loop ([s s])
(datum-map s
f
(lambda (tail? v)
(cond
[(syntax? v) (d->s v (loop (s-e v)))]
[else (f tail? v)]))
seen)))

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -0,0 +1,26 @@
#lang typed/syndicate
(assertion-struct ping : Ping (v))
(assertion-struct pong : Pong (v))
(assertion-struct flip : Flip (v))
(assertion-struct flop : Flop (v))
(define-type-alias Pinger (Ping Int))
(define-type-alias Ponger (U (Ping Int)
(Pong Int)
(Observe (Ping ★/t))))
(define-type-alias PingPong (U Pinger Ponger))
(define-type-alias Flipper (Flip Int))
(define-type-alias Flopper (U (Flip Int)
(Flop Int)
(Observe (Flip ★/t))))
(define-type-alias FlipFlop (U Flipper Flopper))
(run-ground-dataspace (U PingPong FlipFlop)
(spawn Pinger (start-facet _ (assert (ping 5))))
(spawn Ponger (start-facet _ (during (ping $v) (assert (pong v)))))
(spawn Flipper (start-facet _ (assert (flip 8))))
(spawn Flopper (start-facet _ (during (flip $v) (assert (flop v))))))

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(run-ground-dataspace (U)
(spawn (U)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -0,0 +1,22 @@
#lang typed/syndicate
(require rackunit/turnstile)
(typecheck-fail (spawn
(start-facet x
(on (asserted $x:Int)
#f)))
#:with-msg "overly broad interest")
(typecheck-fail (spawn
(start-facet x
(on (asserted (observe $x:Int))
#f)))
#:with-msg "overly broad interest")
;; TODO - but this one seems fine?
(typecheck-fail (spawn
(start-facet x
(on (asserted _)
#f)))
#:with-msg "overly broad interest")

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)

View File

@ -1,4 +1,4 @@
#lang typed/syndicate/roles
#lang typed/syndicate
(require rackunit/turnstile)
@ -15,7 +15,7 @@
(start-facet _
(on (asserted (tuple $x:Int))
(add1 x))))
#:with-msg "spawn: Not prepared to handle inputs:\n\\(Tuple- String\\)")
#:with-msg "spawn: Not prepared to handle inputs:\n\\(Tuple String\\)")
(check-type
(spawn (U)
@ -32,4 +32,4 @@
(know (tuple "hi"))
(on (know (tuple $x:Int))
(add1 x))))
#:with-msg "spawn: Not prepared to handle internal events:\n\\(Tuple- String\\)")
#:with-msg "spawn: Not prepared to handle internal events:\n\\(Tuple String\\)")