Skip to content

Add parallel binding spec (v2)#85

Open
eutro wants to merge 1 commit intomichaelballantyne:v2from
eutro:parallel-binding-spec
Open

Add parallel binding spec (v2)#85
eutro wants to merge 1 commit intomichaelballantyne:v2from
eutro:parallel-binding-spec

Conversation

@eutro
Copy link

@eutro eutro commented Jul 23, 2025

This is to nest what let is to let*, and is precisely the binding structure that Qi needs for tee and friends.

This PR targets syntax-spec-v2, for e.g. prototyping Qi.

This is to `nest` what `let` is to `let*`, and is precisely the binding structure that Qi needs for tee and friends.
@michaelballantyne
Copy link
Owner

Thanks, this is interesting!

I'm not 100% sold in the current form. One of the nice things about nest right now is that you can understand nesting to reduce to simple scopes and bindings.

With the declaration:

(nonterminal expr
  (let* (bp:binding-pair ...) e:expr)
  #:binding (nest bp ... e))

(nonterminal/nesting binding-pair (nested)
  [v:var e:expr]
  #:binding [e (scope (bind v) nested)])

Then you can think through something like the following reduction, at least with the syntax-spec-v3 forms:

(interpret-binding expr #'(let* ([x 1] [y 2] [x 3]) x))
-> interpret the expr nonterminal
(nest (interpret-binding binding-pair #'[x 1])
      (interpret-binding binding-pair #'[y 2])
      (interpret-binding binding-pair #'[z 3])
      (interpret-binding expr #'x))
-> interpret the three instances of the binding-pair nonterminal
(nest [(interpret-binding expr #'1) (scope (bind #'x) nested)]
      [(interpret-binding expr #'2) (scope (bind #'y) nested)]
      [(interpret-binding expr #'3) (scope (bind #'x) nested)]
      (interpret-binding expr #'x))
-> interpret the four instances of the expr nonterminal, with the first 3 reducing to nothing
(nest (scope (bind #'x) nested)
      (scope (bind #'y) nested)
      (scope (bind #'x) nested)
      (ref var #'x))
-> rewrite the variadic nest as binary nesting
(nest (scope (bind #'x) nested)
      (nest (scope (bind #'y) nested)
            (nest (scope (bind #'x) nested)
                  (ref var #'x))))
-> reduce nests by substituting the second argument of nest for `nested` in the first argument
(scope (bind #'x)
   (scope (bind #'y)
      (scope (bind #'x)
             (ref var #'x))))

In the end you've interpreted the program to produce a tree of scopes with bindings and references. All the complex nest stuff goes away. Or, equivalently, the semantics of nest is easily defined via reduction rules in the binding language. And the representation at the end is also basically something that's within the binding rule language.

This parallel proposal is adding something like nest, but unlike with nest there isn't a way to locally express the graph-structured scope that it produces using the more basic scope and bind forms of the language.

@eutro
Copy link
Author

eutro commented Jul 24, 2025

I see what you mean, and when porting to v3 I noticed that parallel is somewhat unsatisfying of a form w.r.t. the nest elaboration it does. I think it might be beneficial to have new "primitive" binding forms to be able to express more DAG-structured binding graphs. I think we can agree that Qi has a compelling use-case (also does Racket's own parallel-let require it? I'm not sure), and the primitives could be more expressive than parallel to allow for such elaborations/normalisations of the binding spec tree.

I've conceived of an alternate approach where one could e.g. delimit the scoping rules and later locally forget or recall (or mask/unmask) the bindings up to the delimiter, or even between two delimiters. Something like

(parallel
 (scope (bind #'x) nested)
 (scope (bind #'y) nested)
 (scope (bind #'z) nested)
 (ref var #'z))
 ~>
(delimit
 (scope (bind #'x)
  (forget
   (scope (bind #'y)
    (forget
     (scope (bind #'z)
      (remember
       (ref var #'x))))))))

(but probably with some way to tag the delimiters to allow for multiple)

I believe with an adequate combination of delimits (or a similar API) one could express arbitrary DAG binding structures.

@michaelballantyne
Copy link
Owner

I unfortunately am not going to have more time to spend on considering this until after my dissertation defense August 15.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants