Re: [racket-users] Macro help

2019-05-25 Thread yfzhe
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

2019-05-24 Thread Kevin Forchione



> 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

2019-05-24 Thread Stephen Chang
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

2019-05-24 Thread Matthias Felleisen

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

2019-05-23 Thread Michael Murdock MacLeod
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

2019-05-23 Thread Kevin Forchione
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?

2019-02-16 Thread David Storrs
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?

2019-02-15 Thread Alexis King
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?

2019-02-15 Thread Jon Zeppieri
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?

2019-02-15 Thread David Storrs
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.