diff --git a/qi-lib/flow/compiler.rkt b/qi-lib/flow/compiler.rkt index 823452eb4..df8c4e772 100644 --- a/qi-lib/flow/compiler.rkt +++ b/qi-lib/flow/compiler.rkt @@ -63,8 +63,10 @@ #'(disjoin (qi0->racket onex) ...)] [((~datum not) onex:clause) #'(negate (qi0->racket onex))] + [((~datum gen)) + #'*->1] [((~datum gen) ex:expr ...) - #'(λ _ (values ex ...))] + #'(thunk* (values ex ...))] [(~or* (~datum NOT) (~datum !)) #'not] [(~or* (~datum AND) (~datum &)) @@ -91,7 +93,7 @@ ;;; Core routing elements [(~or* (~datum ⏚) (~datum ground)) - #'(qi0->racket (select))] + #'*->1] [((~or* (~datum ~>) (~datum thread)) onex:clause ...) #`(compose . #,(reverse (syntax->list @@ -101,15 +103,15 @@ #'(qi0->racket (~> ▽ reverse △))] [((~or* (~datum ==) (~datum relay)) onex:clause ...) #'(relay (qi0->racket onex) ...)] + [((~or* (~datum ==*) (~datum relay*))) + #'1->1] + [((~or* (~datum ==*) (~datum relay*)) onex:clause) + #'(qi0->racket onex)] [((~or* (~datum ==*) (~datum relay*)) onex:clause ... rest-onex:clause) (with-syntax ([len #`#,(length (syntax->list #'(onex ...)))]) #'(qi0->racket (group len (== onex ...) rest-onex) ))] [((~or* (~datum -<) (~datum tee)) onex:clause ...) - #'(λ args - (apply values - (append (values->list - (apply (qi0->racket onex) args)) - ...)))] + #'(tee (qi0->racket onex) ...)] [e:select-form (select-parser #'e)] [e:block-form (block-parser #'e)] [((~datum bundle) (n:number ...) @@ -303,11 +305,15 @@ the DSL. "list?" _)))] [(_ onex:clause) - #'(λ (v . vs) - ((qi0->racket (~> △ (>< (apply (qi0->racket onex) _ vs)))) v))])) + #'(let ([compiled-sep-flow + (λ (v . vs) + ((qi0->racket (~> △ (>< (apply (qi0->racket onex) _ vs)))) + v))]) + compiled-sep-flow)])) (define (select-parser stx) (syntax-parse stx + [(_) #'*->1] [(_ n:number ...) #'(qi0->racket (-< (esc (arg n)) ...))] [(_ arg ...) ; error handling catch-all (report-syntax-error 'select @@ -333,8 +339,10 @@ the DSL. (qi0->racket remainder-onex) n)] [_:id - #'(λ (n selection-flo remainder-flo . vs) - (apply (qi0->racket (group n selection-flo remainder-flo)) vs))] + #'(let ([compiled-group-flow + (λ (n selection-flo remainder-flo . vs) + (apply (qi0->racket (group n selection-flo remainder-flo)) vs))]) + compiled-group-flow)] [(_ arg ...) ; error handling catch-all (report-syntax-error 'group (syntax->datum #'(arg ...)) @@ -342,7 +350,7 @@ the DSL. (define (switch-parser stx) (syntax-parse stx - [(_) #'(qi0->racket _)] + [(_) #'values] [(_ ((~or* (~datum divert) (~datum %)) condition-gate:clause consequent-gate:clause)) @@ -411,10 +419,12 @@ the DSL. #'(qi0->racket (-< (~> (pass condition) sonex) (~> (pass (not condition)) ronex)))] [_:id - #'(λ (condition sonex ronex . args) - (apply (qi0->racket (-< (~> (pass condition) sonex) - (~> (pass (not condition)) ronex))) - args))] + #'(let ([compiled-sieve-flow + (λ (condition sonex ronex . args) + (apply (qi0->racket (-< (~> (pass condition) sonex) + (~> (pass (not condition)) ronex))) + args))]) + compiled-sieve-flow)] [(_ arg ...) ; error handling catch-all (report-syntax-error 'sieve (syntax->datum #'(arg ...)) @@ -435,14 +445,16 @@ the DSL. [(_ flo [error-condition-flo error-handler-flo] ...+) - #'(λ args - (with-handlers ([(qi0->racket error-condition-flo) - (λ (e) - ;; TODO: may be good to support reference to the - ;; error via a binding / syntax parameter - (apply (qi0->racket error-handler-flo) args))] - ...) - (apply (qi0->racket flo) args)))] + #'(let ([compiled-try-flow + (λ args + (with-handlers ([(qi0->racket error-condition-flo) + (λ (e) + ;; TODO: may be good to support reference to the + ;; error via a binding / syntax parameter + (apply (qi0->racket error-handler-flo) args))] + ...) + (apply (qi0->racket flo) args)))]) + compiled-try-flow)] [(_ arg ...) (report-syntax-error 'try (syntax->datum #'(arg ...)) @@ -473,32 +485,44 @@ the DSL. (syntax-parse stx [(_ consequent:clause alternative:clause) - #'(λ (f . args) - (if (apply f args) - (apply (qi0->racket consequent) args) - (apply (qi0->racket alternative) args)))] + #'(let ([compiled-if-flow + (λ (f . args) + (if (apply f args) + (apply (qi0->racket consequent) args) + (apply (qi0->racket alternative) args)))]) + compiled-if-flow)] [(_ condition:clause consequent:clause alternative:clause) - #'(λ args - (if (apply (qi0->racket condition) args) - (apply (qi0->racket consequent) args) - (apply (qi0->racket alternative) args)))])) + #'(let ([compiled-if-flow + (λ args + (if (apply (qi0->racket condition) args) + (apply (qi0->racket consequent) args) + (apply (qi0->racket alternative) args)))]) + compiled-if-flow)])) (define (fanout-parser stx) (syntax-parse stx [_:id #'repeat-values] + [(_ 0) #'*->1] + [(_ 1) #'values] [(_ n:number) ;; a slightly more efficient compile-time implementation ;; for literally indicated N - #`(λ args - (apply values - (append #,@(make-list (syntax->datum #'n) 'args))) )] - [(_ n:expr) - #'(lambda args - (apply values - (apply append - (make-list n args))))])) + #`(let ([compiled-fanout-flow + (λ args + (apply values + (append #,@(make-list (syntax->datum #'n) 'args))))]) + compiled-fanout-flow)] + [(_ e:expr) + #'(let ([n e]) + (case n + [(0) *->1] + [(1) values] + [else + (procedure-rename + (curry repeat-values n) + 'compiled-fanout-flow)]))])) (define (feedback-parser stx) (syntax-parse stx @@ -510,9 +534,10 @@ the DSL. (qi0->racket thenex))] [(_ ((~datum while) tilex:clause) ((~datum then) thenex:clause)) - #'(λ (f . args) - (apply (qi0->racket (feedback (while tilex) (then thenex) f)) - args))] + #'(let ([compiled-feedback-flow + (λ (f . args) + (apply (qi0->racket (feedback (while tilex) (then thenex) f)) args))]) + compiled-feedback-flow)] [(_ ((~datum while) tilex:clause) onex:clause) #'(qi0->racket (feedback (while tilex) (then _) onex))] [(_ ((~datum while) tilex:clause)) @@ -523,17 +548,23 @@ the DSL. #'(feedback-times (qi0->racket onex) n (qi0->racket thenex))] [(_ n:expr ((~datum then) thenex:clause)) - #'(λ (f . args) - (apply (qi0->racket (feedback n (then thenex) f)) args))] + #'(let ([compiled-feedback-flow + (λ (f . args) + (apply (qi0->racket (feedback n (then thenex) f)) args))]) + compiled-feedback-flow)] [(_ n:expr onex:clause) #'(qi0->racket (feedback n (then _) onex))] [(_ onex:clause) - #'(λ (n . args) - (apply (qi0->racket (feedback n onex)) args))] + #'(let ([compiled-feedback-flow + (λ (n . args) + (apply (qi0->racket (feedback n onex)) args))]) + compiled-feedback-flow)] [_:id - #'(λ (n flo . args) - (apply (qi0->racket (feedback n flo)) - args))])) + #'(let ([compiled-feedback-flow + (λ (n flo . args) + (apply (qi0->racket (feedback n flo)) + args))]) + compiled-feedback-flow)])) (define (side-effect-parser stx) (syntax-parse stx @@ -549,7 +580,9 @@ the DSL. [_:id #'map-values] [(_ onex:clause) - #'(curry map-values (qi0->racket onex))] + #'(procedure-rename + (curry map-values (qi0->racket onex)) + 'compiled-amp-flow)] [(_ onex0:clause onex:clause ...) (report-syntax-error 'amp @@ -562,7 +595,9 @@ the DSL. [_:id #'filter-values] [(_ onex:clause) - #'(curry filter-values (qi0->racket onex))])) + #'(procedure-rename + (curry filter-values (qi0->racket onex)) + 'compiled-pass-flow)])) (define (fold-left-parser stx) (syntax-parse stx @@ -607,18 +642,23 @@ the DSL. (syntax-parse stx [_:id #:do [(define chirality (syntax-property stx 'chirality))] - (if (and chirality (eq? chirality 'right)) - #'(λ (f . args) (apply curryr f args)) - #'(λ (f . args) (apply curry f args)))] + #`(let ([compiled-clos-flow + (λ (f . args) + (apply #,(if (and chirality (eq? chirality 'right)) + #'curryr #'curry) + f args))]) + compiled-clos-flow)] [(_ onex:clause) #:do [(define chirality (syntax-property stx 'chirality))] - (if (and chirality (eq? chirality 'right)) - #'(λ args - (qi0->racket (~> (-< _ (~> (gen args) △)) - onex))) - #'(λ args - (qi0->racket (~> (-< (~> (gen args) △) _) - onex))))])) + #`(let ([compiled-clos-flow + (λ args + (qi0->racket + (~> + #,(if (and chirality (eq? chirality 'right)) + #'(-< _ (~> (gen args) △)) + #'(-< (~> (gen args) △) _)) + onex)))]) + compiled-clos-flow)])) (define (literal-parser stx) (syntax-parse stx diff --git a/qi-lib/flow/impl.rkt b/qi-lib/flow/impl.rkt index 679b64649..331008cf9 100644 --- a/qi-lib/flow/impl.rkt +++ b/qi-lib/flow/impl.rkt @@ -10,7 +10,10 @@ map-values filter-values partition-values + 1->1 + *->1 relay + tee loom-compose parity-xor arg @@ -40,22 +43,22 @@ ;; we use a lambda to capture the arguments at runtime ;; since they aren't available at compile time -(define (loom-compose f g [n #f]) - (let ([n (or n (procedure-arity f))]) +(define (loom-compose f g [n (procedure-arity f)]) + (define compiled-group-flow (λ args - (let ([num-args (length args)]) - (if (< num-args n) - (if (= 0 num-args) - (values) - (error 'group (~a "Can't select " - n - " arguments from " - args))) - (let ([sargs (take args n)] - [rargs (drop args n)]) - (apply values - (append (values->list (apply f sargs)) - (values->list (apply g rargs)))))))))) + (define num-args (length args)) + (if (< num-args n) + (if (= 0 num-args) + (values) + (error 'group (~a "Can't select " + n + " arguments from " + args))) + (let-values ([(sargs rargs) (split-at args n)]) + (apply values + (append (values->list (apply f sargs)) + (values->list (apply g rargs)))))))) + compiled-group-flow) (define (parity-xor . args) (and (foldl xor #f args) #t)) @@ -165,7 +168,7 @@ [b (in-value (cdr c+b))] [args (in-value (hash-ref by-cs c))]) (call-with-values (λ () (apply b args)) list))) - (apply values (apply append results))) + (apply values (append* results))) (define (->boolean v) (and v #t)) (define true. (thunk* #t)) @@ -186,6 +189,9 @@ (append (values->list (apply op vs)) (apply zip-with op (map rest seqs)))))) +(define 1->1 (thunk (values))) +(define *->1 (thunk* (values))) + ;; from mischief/function - requiring it runs aground ;; of some "name is protected" error while building docs, not sure why; ;; so including the implementation directly here for now @@ -194,9 +200,26 @@ (lambda (ks vs f . xs) (keyword-apply f ks vs xs)))) -(define (relay . fs) - (λ args - (apply values (zip-with call fs args)))) +(define relay + (case-lambda + [() 1->1] + [(f) (procedure-reduce-arity-mask f 2)] + [fs + (define (compiled-relay-flow . args) + (apply values (zip-with call fs args))) + compiled-relay-flow])) + +(define (tee . fs) + (match (remq* (list *->1) fs) + ['() *->1] + [`(,f) f] + [fs + (define (compiled-tee-flow . args) + (apply values + (append* + (for/list ([f (in-list fs)]) + (values->list (apply f args)))))) + compiled-tee-flow])) (define (all? . args) (and (for/and ([v (in-list args)]) v) #t)) @@ -208,7 +231,7 @@ (not (for/or ([v (in-list args)]) v))) (define (repeat-values n . vs) - (apply values (apply append (make-list n vs)))) + (apply values (append* (make-list n vs)))) (define (power n f) (apply compose (make-list n f))) diff --git a/qi-test/tests/flow.rkt b/qi-test/tests/flow.rkt index 7f41fe490..2d596cebb 100644 --- a/qi-test/tests/flow.rkt +++ b/qi-test/tests/flow.rkt @@ -242,6 +242,9 @@ "separate into a non-primitive flow with presupplied values")) (test-suite "gen" + (check-eq? (☯ (gen)) (☯ ⏚)) + (check-eq? ((☯ (~> (gen) ▽))) + '()) (check-equal? ((☯ (gen 5))) 5) (check-equal? ((☯ (gen 5)) 3) @@ -420,6 +423,11 @@ "a")) (test-suite "-<" + (check-eq? (☯ (-<)) (☯ ⏚)) + (check-eq? (☯ (-< add1 (-<))) + add1) + (check-eq? (☯ (-< (-<) add1)) + add1) (check-equal? ((☯ (~> (-< sqr add1) ▽)) 5) (list 25 6)) @@ -452,6 +460,8 @@ "named tee junction form")) (test-suite "==" + (check-eq? ((☯ (~> (==) ▽))) + '()) (check-equal? ((☯ (~> (== sqr add1) ▽)) 5 7) (list 25 8)) @@ -476,7 +486,11 @@ 5 7) (list 5 5 8) "relay with arity-increasing clause") - (check-exn exn:fail? + (check-eq? ((☯ (~> (== add1))) 1) 2) + (check-exn exn:fail:contract:arity? + (thunk ((☯ (~> (== +))) + 1 2 3))) + (check-exn exn:fail:contract:arity? (thunk ((☯ (~> (== ⏚ add1) ▽)) 5 7 8)) "relay elements must be in one-to-one correspondence with input") @@ -486,6 +500,14 @@ "named relay form")) (test-suite "==*" + (check-eq? ((☯ (~> (==*) ▽))) + '()) + (check-eq? ((☯ (~> (==* +))) + 1 2 3 4 5) + 15) + (check-eq? ((☯ (~> (==* (~> +)))) + 1 2 3 4 5) + 15) (check-equal? ((☯ (~> (==* add1 sub1 +) ▽)) 1 1 1 1 1) (list 2 0 3)) @@ -503,7 +525,9 @@ 6) (check-equal? ((☯ (-< ground add1)) 5) - 6))) + 6) + (check-eq? (☯ (-< ⏚ add1)) add1) + (check-eq? (☯ ⏚) (☯ (-<))))) (test-suite "Exceptions" @@ -939,6 +963,9 @@ (check-equal? (~> (2 3) (fanout 0) ▽) null "N=0 produces no values.") + (check-equal? (~> (2 3) (fanout 1) ▽) + (list 2 3) + "N=1 produces original values.") (check-equal? (~> () (fanout 3) ▽) null "No inputs produces no outputs.") @@ -1051,6 +1078,7 @@ "grouping more inputs than are available shows a helpful error")) (test-suite "select" + (check-equal? (☯ (~> (select))) (☯ ⏚)) (check-equal? ((☯ (~> (select) ▽)) 1) null)