Re: [racket-users] Macro help
Another "syntax-case-y" version generates `define-values`: (define-syntax-rule (aux clause ...) (aux-helper () (clause ...))) (define-syntax (aux-helper stx) (syntax-case stx () [(_ ([id val] ...) ()) #'(define-values (id ...) (values val ...))] [(_ (id+val ...) ([id val] more ...)) (identifier? #'id) #'(aux-helper (id+val ... [id val]) (more ...))] [(_ (id+val ...) (id more ...)) (identifier? #'id) #'(aux-helper (id+val ... [id #f]) (more ...))])) 在 2019年5月24日星期五 UTC+8下午12:41:20,lysseus写道: > > Hi guys, > I’ve been wracking my brains all day trying to come up with a macro that > would convert this syntax: > > ;; (aux a (b (* 2 pi)) c (d pi)) > ;; => (define-values (a b c d) (values #f 6.28318530717958 #f > 3.141592653589793) > > > I’m missing some part of the picture. The closest I’ve come is to create a > list of the pairs: > > #lang racket > > (define-syntax (aux stx) > (syntax-case stx () > [(_ (var val)) #'`((var ,val))] > [(_ var) #''((var #f))] > [(_ (var0 val0) var1 ...) #'(append `((var0 ,val0)) (aux var1 ...))] > [(_ var0 var1 ...) #'(append '((var0 #f)) (aux var1 ...))])) > > (aux a (b (* 2 pi)) c (d pi)) ;=> '((a #f) (b 6.283185307179586) (c #f) (d > 3.141592653589793)) > > > Any help is greatly appreciated! > > Kevin -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/38e8096f-0831-43be-879c-86fb9252004f%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Re: [racket-users] Macro help
> On May 24, 2019, at 7:35 AM, Stephen Chang wrote: > > If `define-values` is not strictly required, here's a more > syntax-case-y recursive version: > > (define-syntax (aux stx) > (syntax-case stx () >[(_) #'(begin)] >[(_ (var val) . rst) > (identifier? #'var) > #'(begin > (define var val) > (aux . rst))] >[(_ var . rst) > (identifier? #'var) > #'(begin > (define var #f) > (aux . rst))])) > > On Fri, May 24, 2019 at 9:33 AM Matthias Felleisen > wrote: >> >> >> Let me propose the use of syntax-parse as an alternative here. I think it >> clarifies the purpose of the maco. — Matthias >> >> >> #lang racket >> >> (require (for-syntax syntax/parse)) >> >> #; (aux a (b (* 2 pi)) c (d pi)) >> ;; => >> #; (define-values (a b c d) (values #f 6.28318530717958 #f >> 3.141592653589793)) >> >> >> (begin-for-syntax >> (define-syntax-class optionally-initiliazed >>(pattern (lhs:id rhs:expr)) >>(pattern lhs:id #:attr rhs #'#f))) >> >> (define-syntax (aux stx) >> (syntax-parse stx >>[(_ p:optionally-initiliazed ...) #'(define-values (p.lhs ...) (values >> p.rhs ...))])) >> >> (aux a (b (* 2 pi)) c (d pi)) >> >> >> >> >> On May 24, 2019, at 12:52 AM, Michael Murdock MacLeod >> wrote: >> >> Does this work? It uses a helper function, `prune`, to parse the var-val >> clauses. >> >> #lang racket >> >> (define-for-syntax (prune stx) >> (syntax-case stx () >> [() >>#'()] >> [((var val) others ...) >>(cons #'(var val) >> (prune #'(others ...)))] >> [(var others ...) >>(cons #'(var #f) >> (prune #'(others ...)))])) >> >> (define-syntax (aux stx) >> (syntax-case stx () >> [(_ terms ...) >>(with-syntax ([((var val) ...) (prune #'(terms ...))]) >> #'(define-values (var ...) (values val ...)))])) >> >> (aux a (b (* 2 pi)) c (d pi)) >> a >> b >> c >> d >> >> ;; output shown below >> >> #f >> 6.283185307179586 >> #f >> 3.141592653589793 Wow! Thanks so much guys. Each of these solutions adds another brick to the wall of my macro eduction! Kevin -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/88FB1562-1F78-434E-8D6A-78DE8D86C909%40gmail.com. For more options, visit https://groups.google.com/d/optout.
Re: [racket-users] Macro help
If `define-values` is not strictly required, here's a more syntax-case-y recursive version: (define-syntax (aux stx) (syntax-case stx () [(_) #'(begin)] [(_ (var val) . rst) (identifier? #'var) #'(begin (define var val) (aux . rst))] [(_ var . rst) (identifier? #'var) #'(begin (define var #f) (aux . rst))])) On Fri, May 24, 2019 at 9:33 AM Matthias Felleisen wrote: > > > Let me propose the use of syntax-parse as an alternative here. I think it > clarifies the purpose of the maco. — Matthias > > > #lang racket > > (require (for-syntax syntax/parse)) > > #; (aux a (b (* 2 pi)) c (d pi)) > ;; => > #; (define-values (a b c d) (values #f 6.28318530717958 #f 3.141592653589793)) > > > (begin-for-syntax > (define-syntax-class optionally-initiliazed > (pattern (lhs:id rhs:expr)) > (pattern lhs:id #:attr rhs #'#f))) > > (define-syntax (aux stx) > (syntax-parse stx > [(_ p:optionally-initiliazed ...) #'(define-values (p.lhs ...) (values > p.rhs ...))])) > > (aux a (b (* 2 pi)) c (d pi)) > > > > > On May 24, 2019, at 12:52 AM, Michael Murdock MacLeod > wrote: > > Does this work? It uses a helper function, `prune`, to parse the var-val > clauses. > > #lang racket > > (define-for-syntax (prune stx) > (syntax-case stx () >[() > #'()] >[((var val) others ...) > (cons #'(var val) > (prune #'(others ...)))] >[(var others ...) > (cons #'(var #f) > (prune #'(others ...)))])) > > (define-syntax (aux stx) > (syntax-case stx () >[(_ terms ...) > (with-syntax ([((var val) ...) (prune #'(terms ...))]) > #'(define-values (var ...) (values val ...)))])) > > (aux a (b (* 2 pi)) c (d pi)) > a > b > c > d > > ;; output shown below > > #f > 6.283185307179586 > #f > 3.141592653589793 > > On Thursday, May 23, 2019 9:41:17 PM PDT Kevin Forchione wrote: > > Hi guys, > I’ve been wracking my brains all day trying to come up with a macro that > would convert this syntax: > > ;; (aux a (b (* 2 pi)) c (d pi)) > ;; => (define-values (a b c d) (values #f 6.28318530717958 #f > 3.141592653589793) > > > I’m missing some part of the picture. The closest I’ve come is to create a > list of the pairs: > > #lang racket > > (define-syntax (aux stx) > (syntax-case stx () >[(_ (var val)) #'`((var ,val))] >[(_ var) #''((var #f))] >[(_ (var0 val0) var1 ...) #'(append `((var0 ,val0)) (aux var1 ...))] >[(_ var0 var1 ...) #'(append '((var0 #f)) (aux var1 ...))])) > > (aux a (b (* 2 pi)) c (d pi)) ;=> '((a #f) (b 6.283185307179586) (c #f) (d > 3.141592653589793)) > > > Any help is greatly appreciated! > > Kevin > > > > -- > You received this message because you are subscribed to the Google Groups > "Racket Users" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to racket-users+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/racket-users/2893163.LJ05K77S5N%40alphtsr. > For more options, visit https://groups.google.com/d/optout. > > > -- > You received this message because you are subscribed to the Google Groups > "Racket Users" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to racket-users+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/racket-users/A0B65B5E-0244-4E7C-89E5-24ED2CDF69EB%40felleisen.org. > For more options, visit https://groups.google.com/d/optout. -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/CAFfiA1%2BDYBrq_UutmwX5_sPatngCr%3DLkqFsczLxKn_rDBA%2BOrw%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.
Re: [racket-users] Macro help
Let me propose the use of syntax-parse as an alternative here. I think it clarifies the purpose of the maco. — Matthias #lang racket (require (for-syntax syntax/parse)) #; (aux a (b (* 2 pi)) c (d pi)) ;; => #; (define-values (a b c d) (values #f 6.28318530717958 #f 3.141592653589793)) (begin-for-syntax (define-syntax-class optionally-initiliazed (pattern (lhs:id rhs:expr)) (pattern lhs:id #:attr rhs #'#f))) (define-syntax (aux stx) (syntax-parse stx [(_ p:optionally-initiliazed ...) #'(define-values (p.lhs ...) (values p.rhs ...))])) (aux a (b (* 2 pi)) c (d pi)) > On May 24, 2019, at 12:52 AM, Michael Murdock MacLeod > wrote: > > Does this work? It uses a helper function, `prune`, to parse the var-val > clauses. > > #lang racket > > (define-for-syntax (prune stx) > (syntax-case stx () >[() > #'()] >[((var val) others ...) > (cons #'(var val) > (prune #'(others ...)))] >[(var others ...) > (cons #'(var #f) > (prune #'(others ...)))])) > > (define-syntax (aux stx) > (syntax-case stx () >[(_ terms ...) > (with-syntax ([((var val) ...) (prune #'(terms ...))]) > #'(define-values (var ...) (values val ...)))])) > > (aux a (b (* 2 pi)) c (d pi)) > a > b > c > d > > ;; output shown below > > #f > 6.283185307179586 > #f > 3.141592653589793 > > On Thursday, May 23, 2019 9:41:17 PM PDT Kevin Forchione wrote: >> Hi guys, >> I’ve been wracking my brains all day trying to come up with a macro that >> would convert this syntax: >> >> ;; (aux a (b (* 2 pi)) c (d pi)) >> ;; => (define-values (a b c d) (values #f 6.28318530717958 #f >> 3.141592653589793) >> >> >> I’m missing some part of the picture. The closest I’ve come is to create a >> list of the pairs: >> >> #lang racket >> >> (define-syntax (aux stx) >> (syntax-case stx () >>[(_ (var val)) #'`((var ,val))] >>[(_ var) #''((var #f))] >>[(_ (var0 val0) var1 ...) #'(append `((var0 ,val0)) (aux var1 ...))] >>[(_ var0 var1 ...) #'(append '((var0 #f)) (aux var1 ...))])) >> >> (aux a (b (* 2 pi)) c (d pi)) ;=> '((a #f) (b 6.283185307179586) (c #f) (d >> 3.141592653589793)) >> >> >> Any help is greatly appreciated! >> >> Kevin > > > -- > You received this message because you are subscribed to the Google Groups > "Racket Users" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to racket-users+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/racket-users/2893163.LJ05K77S5N%40alphtsr. > For more options, visit https://groups.google.com/d/optout. -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/A0B65B5E-0244-4E7C-89E5-24ED2CDF69EB%40felleisen.org. For more options, visit https://groups.google.com/d/optout.
Re: [racket-users] Macro help
Does this work? It uses a helper function, `prune`, to parse the var-val clauses. #lang racket (define-for-syntax (prune stx) (syntax-case stx () [() #'()] [((var val) others ...) (cons #'(var val) (prune #'(others ...)))] [(var others ...) (cons #'(var #f) (prune #'(others ...)))])) (define-syntax (aux stx) (syntax-case stx () [(_ terms ...) (with-syntax ([((var val) ...) (prune #'(terms ...))]) #'(define-values (var ...) (values val ...)))])) (aux a (b (* 2 pi)) c (d pi)) a b c d ;; output shown below #f 6.283185307179586 #f 3.141592653589793 On Thursday, May 23, 2019 9:41:17 PM PDT Kevin Forchione wrote: > Hi guys, > I’ve been wracking my brains all day trying to come up with a macro that > would convert this syntax: > > ;; (aux a (b (* 2 pi)) c (d pi)) > ;; => (define-values (a b c d) (values #f 6.28318530717958 #f > 3.141592653589793) > > > I’m missing some part of the picture. The closest I’ve come is to create a > list of the pairs: > > #lang racket > > (define-syntax (aux stx) > (syntax-case stx () > [(_ (var val)) #'`((var ,val))] > [(_ var) #''((var #f))] > [(_ (var0 val0) var1 ...) #'(append `((var0 ,val0)) (aux var1 ...))] > [(_ var0 var1 ...) #'(append '((var0 #f)) (aux var1 ...))])) > > (aux a (b (* 2 pi)) c (d pi)) ;=> '((a #f) (b 6.283185307179586) (c #f) (d > 3.141592653589793)) > > > Any help is greatly appreciated! > > Kevin -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/2893163.LJ05K77S5N%40alphtsr. For more options, visit https://groups.google.com/d/optout.
[racket-users] Macro help
Hi guys, I’ve been wracking my brains all day trying to come up with a macro that would convert this syntax: ;; (aux a (b (* 2 pi)) c (d pi)) ;; => (define-values (a b c d) (values #f 6.28318530717958 #f 3.141592653589793) I’m missing some part of the picture. The closest I’ve come is to create a list of the pairs: #lang racket (define-syntax (aux stx) (syntax-case stx () [(_ (var val)) #'`((var ,val))] [(_ var) #''((var #f))] [(_ (var0 val0) var1 ...) #'(append `((var0 ,val0)) (aux var1 ...))] [(_ var0 var1 ...) #'(append '((var0 #f)) (aux var1 ...))])) (aux a (b (* 2 pi)) c (d pi)) ;=> '((a #f) (b 6.283185307179586) (c #f) (d 3.141592653589793)) Any help is greatly appreciated! Kevin -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/AC605DEF-0AC6-4590-B53F-700C6AE14D4D%40gmail.com. For more options, visit https://groups.google.com/d/optout.
Re: [racket-users] [macro help] How can I render a parenthesized set of elements optional?
Thank you both -- Jon for the code and Alexis for the thorough description. Alexis, this was exactly what I was looking for. I've been frustrated with macros because I kept trying and failing to figure out a mental model of what the heck was going on under the hood. I had some of the basics, but anything more sophisticated I was basically guessing and cargo-culting. I think it would help a lot of future people if your explanation got pasted verbatim into the docs. On Sat, Feb 16, 2019 at 1:53 AM Alexis King wrote: > > Jon is right. Here’s an explanation why. > > Think of `...` as a postfix operator. It repeats what comes before it a > certain number of times. In order for `...` to know how many times to repeat > the previous head template, it looks inside the head template for any > attributes bound at the appropriate ellipsis depth, and it repeats the head > template once for each value of the attributes. The `...` operator > essentially creates a loop, iterating through each value of the attribute. > > The value of attributes bound under ellipses are therefore lists. You can see > this for yourself if you use the `attribute` accessor to explicitly get at > the value of an attribute matched under an ellipsis: > > > (syntax-parse #'(a b c) > [(x:id ...) >(attribute x)]) > '(# > # > #) > > But what happens to the value of an attribute when it is completely > unspecified, since it has been marked `~optional`? If the `~optional` wraps > the whole sequence, such that the ellipsis is inside the `~optional` pattern, > then the attribute is not a list at all, but `#f`: > > > (syntax-parse #'() > [({~optional (x:id ...)}) >(attribute x)]) > #f > > This causes problems. If we were to write #'(x ...) when `x` is bound to > `#f`, then the template will raise an error, since `x` isn’t a list, and > therefore the ellipses don’t know how many times to repeat the preceding > template. > > What you tried to do is silence that error by wrapping the offending template > with `~?`. This is a natural thing to try, but it doesn’t work. Why? Well, > it’s true that {~? x} turns into {~@} when `x` is `#f`, but this does not > matter, since you essentially wrote ({~? x} ...). This means that the `x` > under the ellipsis doesn’t refer to the attribute `x` as a whole, but instead > refers to each *element* of `x`, since `...` creates a loop. So the template > attempts to iterate through the values of (attribute x), but it finds that > value isn’t a list at all, gets confused, and explodes. > > Jon’s fix changes this. It moves the looping *inside* the `~?`, which means > that `~?` is now looking at `x` as a whole (not each element of `x`), and > just skips the loop altogether, avoiding the error. It’s morally the > difference between this code: > > (for/list ([x (in-list (attribute x))]) > (if x x #'{~@})) > > and this code: > > (if (attribute x) > (for/list ([x (in-list (attribute x))]) > x) > #'{~@}) > > --- > > A secondary question: is the template #'({~? x} ...) ever useful? And the > answer is: YES! It just does something different. > > Since #'({~? x} ...) iterates through the values of `x` before checking for > `#f`-ness, then it is useful when `x` itself is never `#f`, but elements of > it may be. This can appear when parsing, say, a list of pairs, where the > second element of each pair is optional: > > > (define parse-pairs > (syntax-parser > [([x:id {~optional n:nat}] ...) > #'([x ...] > [{~? n} ...])])) > > (parse-pairs #'([a 1] [b] [c 3])) > # > > (parse-pairs #'([a] [b] [c])) > # > > Note that when there are no numbers in the input, the output list is still > present but is simply empty, while when some but not all numbers are > provided, the missing numbers are simply skipped in the output. > > --- > > One final point: you may think to yourself “all of this is confusing and > procedural, why should I have to think about attributes?” While I think > understanding what’s going on internally can be helpful, it isn’t strictly > necessary. There’s actually a declarative intuition to guide whether you > should write #'({~? {~@ x ...}}) or #'({~? x} ...). This intuition is as > follows. > > There is a dualism in `syntax-parse`’s pattern language and in the `syntax` > form’s template language. For example: > > - Writing `x` in a pattern, where `x` is an identifier, matches a term, and > writing `x` in a template constructs the same term. > > - Writing (a . b) in a pattern matches a pair, and writing (a . b) in a > template constructs a pair. > > - Writing `p ...` in a pattern matches zero or more occurrences of the > pattern `p`, and writing `t ...` in a template that contains variables bound > in `p` constructs the same number of occurrences in the template. > > To put things another way: > > - Variables in patterns correspond to variables in templates. > -
Re: [racket-users] [macro help] How can I render a parenthesized set of elements optional?
Jon is right. Here’s an explanation why. Think of `...` as a postfix operator. It repeats what comes before it a certain number of times. In order for `...` to know how many times to repeat the previous head template, it looks inside the head template for any attributes bound at the appropriate ellipsis depth, and it repeats the head template once for each value of the attributes. The `...` operator essentially creates a loop, iterating through each value of the attribute. The value of attributes bound under ellipses are therefore lists. You can see this for yourself if you use the `attribute` accessor to explicitly get at the value of an attribute matched under an ellipsis: > (syntax-parse #'(a b c) [(x:id ...) (attribute x)]) '(# # #) But what happens to the value of an attribute when it is completely unspecified, since it has been marked `~optional`? If the `~optional` wraps the whole sequence, such that the ellipsis is inside the `~optional` pattern, then the attribute is not a list at all, but `#f`: > (syntax-parse #'() [({~optional (x:id ...)}) (attribute x)]) #f This causes problems. If we were to write #'(x ...) when `x` is bound to `#f`, then the template will raise an error, since `x` isn’t a list, and therefore the ellipses don’t know how many times to repeat the preceding template. What you tried to do is silence that error by wrapping the offending template with `~?`. This is a natural thing to try, but it doesn’t work. Why? Well, it’s true that {~? x} turns into {~@} when `x` is `#f`, but this does not matter, since you essentially wrote ({~? x} ...). This means that the `x` under the ellipsis doesn’t refer to the attribute `x` as a whole, but instead refers to each *element* of `x`, since `...` creates a loop. So the template attempts to iterate through the values of (attribute x), but it finds that value isn’t a list at all, gets confused, and explodes. Jon’s fix changes this. It moves the looping *inside* the `~?`, which means that `~?` is now looking at `x` as a whole (not each element of `x`), and just skips the loop altogether, avoiding the error. It’s morally the difference between this code: (for/list ([x (in-list (attribute x))]) (if x x #'{~@})) and this code: (if (attribute x) (for/list ([x (in-list (attribute x))]) x) #'{~@}) --- A secondary question: is the template #'({~? x} ...) ever useful? And the answer is: YES! It just does something different. Since #'({~? x} ...) iterates through the values of `x` before checking for `#f`-ness, then it is useful when `x` itself is never `#f`, but elements of it may be. This can appear when parsing, say, a list of pairs, where the second element of each pair is optional: > (define parse-pairs (syntax-parser [([x:id {~optional n:nat}] ...) #'([x ...] [{~? n} ...])])) > (parse-pairs #'([a 1] [b] [c 3])) # > (parse-pairs #'([a] [b] [c])) # Note that when there are no numbers in the input, the output list is still present but is simply empty, while when some but not all numbers are provided, the missing numbers are simply skipped in the output. --- One final point: you may think to yourself “all of this is confusing and procedural, why should I have to think about attributes?” While I think understanding what’s going on internally can be helpful, it isn’t strictly necessary. There’s actually a declarative intuition to guide whether you should write #'({~? {~@ x ...}}) or #'({~? x} ...). This intuition is as follows. There is a dualism in `syntax-parse`’s pattern language and in the `syntax` form’s template language. For example: - Writing `x` in a pattern, where `x` is an identifier, matches a term, and writing `x` in a template constructs the same term. - Writing (a . b) in a pattern matches a pair, and writing (a . b) in a template constructs a pair. - Writing `p ...` in a pattern matches zero or more occurrences of the pattern `p`, and writing `t ...` in a template that contains variables bound in `p` constructs the same number of occurrences in the template. To put things another way: - Variables in patterns correspond to variables in templates. - (a . b) in patterns corresponds to (a . b) in templates. - `...` in patterns corresponds to `...` in templates. This might seem obvious, but it turns out that `~?` and `~@` have cousins in the pattern language, too. They are just less obvious, because their cousins have different names: - `~optional` in patterns corresponds to `~?` in templates. - `~seq` in patterns corresponds to `~@` in templates. (I would have liked `~?` and `~@` to be named `~optional` and `~seq` to make this duality clearer, but sadly, since `~?` and `~@` were added recently, reusing existing names would make them backwards incompatible with older code that expanded to `syntax-parse` patterns.) Since `~optional` corresponds to
Re: [racket-users] [macro help] How can I render a parenthesized set of elements optional?
On Fri, Feb 15, 2019 at 11:50 PM David Storrs wrote: > > #lang racket > (require (for-syntax racket/syntax syntax/parse)) > > (define-syntax (struct++ stx) > (syntax-parse stx > [(_ name:id (field:id ...) (~optional (rule:expr ...)) opt ...) > #'(begin (struct name (field ...) opt ...) > (list (~? rule) ... ))])) > > I think this does what you want: (list (~? (~@ rule ...))) -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
[racket-users] [macro help] How can I render a parenthesized set of elements optional?
I'm twiddling with struct-plus-plus, adding declarative rules, and ~? isn't doing what I expect. I expect that: (~? rule) ... is equivalent to (~? rule (~@)) and means "If `rule` matched something, insert it here. Do this for each item that `rule` matched. If `rule` did not match anything, put nothing here." That's not what I'm seeing, though. The following is as stripped-down as I can get it and still demonstrate the problem. #lang racket (require (for-syntax racket/syntax syntax/parse)) (define-syntax (struct++ stx) (syntax-parse stx [(_ name:id (field:id ...) (~optional (rule:expr ...)) opt ...) #'(begin (struct name (field ...) opt ...) (list (~? rule) ... ))])) Given this declaration, it should be legal to omit the second set of parentheses and their contents, but I'm getting compilation errors when I do that. ; This works fine (struct++ animal (species) ('testing 'testing) #:transparent) (animal 'dog) ; As does this (struct++ person (name age) () #:transparent) (person 'bob 19) ; The lack of the second set of parens causes ; compilation to fail with the error: ; ?: attribute contains non-list value ; value: #f (struct++ person (name age) #:transparent) (person 'bob 19) I can analyze it this far: I'm using the (~? head-template) form here. `rule` is an identifier bound to a pattern variable, so `rule` is a template. All templates are head-templates, therefore `rule` is a head template. There were no rules for `rule` to match so `rule` should be a missing pattern variable within the overall pattern, and therefore the ~? form should emit nothing. Clearly I'm not understanding something. Can someone point me in the right direction? -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.