-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the ability to define bindings #36
Comments
I suggested using boxes in the implementation. I made the changes to the current compiler branch (patch at the end) and ran the benchmarks on both sides. What I saw was surprising. 35/79 (~44.87%) of the benchmarks ran slower. 16 (~20.25%) ran at exactly the same speed. The remaining 28 (~35.44%) ran faster. 48 (~60.76%) have an absolute percentage-change of less than 10%. Those outside the 10% are (roughly)
I might investigate these benchmarks if I have time, but I should note that the absolute difference in these "largest changes" has a spread from 5ms to 28ms. I don't know if any benchmarks use the bindings, which is one place I'd want to really investigate. I also don't know if these numbers include compilation time (which I would expect to suffer mildly from this change). To get the data, I ran Raw data + simple calculations (sample size: one run on each branch
Patch commit 0806b5b1a7d249396b02f8ed6eecd1546c6e1f83
Author: D. Ben Knoble <ben.knoble+github@gmail.com>
Date: Mon Dec 18 10:08:57 2023 -0500
bindings: use a box
diff --git a/qi-lib/flow/core/compiler.rkt b/qi-lib/flow/core/compiler.rkt
index 84adc4e..50ed118 100644
--- a/qi-lib/flow/core/compiler.rkt
+++ b/qi-lib/flow/core/compiler.rkt
@@ -102,8 +102,15 @@ (define (bound-identifiers stx)
;; wrap stx with (let ([v undefined] ...) stx) for v ∈ ids
(define (wrap-with-scopes stx ids)
- (with-syntax ([(v ...) ids])
- #`(let ([v undefined] ...) #,stx)))
+ (with-syntax ([(v- ...) (generate-temporaries ids)]
+ [(v ...) ids])
+ #`(let ([v- (box undefined)] ...)
+ (let-syntax ([v (make-set!-transformer
+ (syntax-parser
+ #:literals {set!}
+ [(set! var val) #'(set-box! v- val)]
+ [var #'(unbox v-)]))] ...)
+ #,stx))))
(define-qi-expansion-step (process-bindings stx)
;; TODO: use syntax-parse and match ~> specifically. |
I started a PR to add any benchmarks that might reveal a difference here, #138 . |
We can leverage Racket's bindings at the top level of Qi flows, but don't have a way to name intermediate values produced in flows, without decomposing the flows themselves and naming the results of each component using Racket definition forms like
define
andlet
.The other option available now is to use "control" inputs, that is, pass parameters for a flow specification as runtime inputs to the flow. This option is perhaps equally expressive, but in some cases it is more complicated than using bindings would be.
Examples
Here are some examples illustrating what bindings in Qi might look like:
equivalent to
(Note: this syntax conflicts with the use of
as
elsewhere.)Context: #29
More generally, the ability to introduce Qi-native bindings in this way allows us to name intermediate values computed in a flow, providing an alternative to the use of "control" inputs to parametrize flow specifications (and would allow us to do this syntactically instead -- e.g.
(~> (6) ... (as n) (feedback n add1))
instead of(~> (-< 5 (gen (flow add1))) feedback)
. See #34 .Bindings syntax options
Implementation
Could we embed Racket's pattern matching language (
match
) into Qi to get bindings with minimal effort? It might not be as flexible in terms of scoping rules though. Just an option to keep in mind, maybe even just as an initial version.cc @michaelballantyne
The text was updated successfully, but these errors were encountered: