Re: [racket-users] Re: I wrote my first macro ever!

2019-04-30 Thread Jérôme Martin
Looks good to me!

On Monday, April 29, 2019 at 9:06:53 PM UTC+2, wanderley...@gmail.com wrote:

> [(_ name (~var e element-exp) ...) 
>

Here you can replace the `~var` call with the more classic form 
`e:element-exp`.
`~var` is only required when you want to pass arguments to the class 
(which, as I was saying, is rare, I discovered it not so long ago).
 

> I am amused by the 
> power of `...` in templates. 
>

 It's definitely powerful! The way `...` works (I'm not gonna explain that 
right so take it with a pinch of salt) is by examining the previous 
expression and trying to find syntax bindings inside that expression that 
have a "depth" and repeat them.
So for example, if you bind a syntax attribute with `#'(element ...)`, you 
give `#'element` a depth of 1 (basic bindings without ellipsis have a depth 
of 0).
If you create a binding inside an ellipsis bound form, you nest it one 
level of depth further: `((element ...) ...)` has depth 2.

If you try using a syntax binding with depth > 0 without attaching `...` 
somewhere at the end, the macro will crash saying that the ellipsis is 
missing.

So basically, you are free to put `...` anywhere as long a the expression 
just before contains syntax bindings with the right levels of depth.

-- 
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.


Re: [racket-users] Re: I wrote my first macro ever!

2019-04-29 Thread wanderley.guimar...@gmail.com
Hi Jérôme, `syntax-parse` was in my list to explore!  Thanks for the
example!  I did a new version following your example

(require
 (for-syntax
  racket/base
  racket/syntax
  syntax/parse))

(begin-for-syntax
  (define-syntax-class (element-exp)
(pattern (element:id value)
 #:with element? (format-id #'element "~a?" #'element)
 #:with var-decl #'(define element value)
 #:with pred-decl #'(define (element? v) (eq? element v)

(define-syntax (define-enum stx)
  (syntax-parse stx
[(_ name (~var e element-exp) ...)
 (with-syntax ([name? (format-id #'name "~a?" #'name)])
   #'(begin
   e.var-decl ...
   e.pred-decl ...
   (define (name? v) (or (e.element? v) ...]))

(module+ test
  (require rackunit)
  (define-enum animal (dog 1) (cat "2"))
  (check-true (dog? dog))
  (check-false (cat? dog))
  (check-true (animal? dog))
  (check-false (animal? 3)))

I removed the prefix and added a function to check if a value is a
valid enum value.  When trying to write the later, I stuck on the
expression

(define (name? v) (or e.element ...))

which I knew was not what I wanted.  Then I though  "You know, I
really liked to write (or (e.element v) ...).  You know what .. why
not give a try?".  I did and it worked as a charm!  I am amused by the
power of `...` in templates.

On Mon, Apr 29, 2019 at 12:37 AM Jérôme Martin
 wrote:
>
> Hello there!
>
> I'm really glad you're picking up Racket and enjoying it!
>
> I've been through the same paths and struggles before, so thank you very much 
> for posting that piece of
> experience here :)
>
> For your information, here is a syntax-parse version of your macro, which I 
> find simpler to write and understand:
> (it's basically syntactic sugar over syntax-case, but it's really useful!)
>
> #lang racket/base
>
> (provide
>   define-enum)
>
> (require
>   (for-syntax
> racket/base
> racket/syntax
> syntax/parse))
>
> (define-syntax (define-enum stx)
>   (syntax-parse stx
> [(_ name (element value) ...)
>  #:with (name-element? ...)
> (datum->syntax
>   #'(element ...)
>   (map (lambda (elem)
>  (format-id elem "~a-~a?" #'name elem))
>(syntax->list #'(element ...
>  #'(begin
>  (define element value) ...
>  (define (name-element? v)
>(eq? element v)) ...)]))
>
> (module+ test
>   (require rackunit)
>
>   (define-enum unit
> (dry #\d)
> (empty #\.)
> (rock #\#)
> (water #\~))
>
>   (check-true (unit-dry? #\d))
>   (check-false (unit-empty? #\#))
>   (check-false (unit-rock? #\~))
>   (check-true (unit-water? #\~)))
>
> To remove that ugly map, which requires transforming syntaxes to list then 
> back to syntaxes, you can use a syntax-class:
>
> (begin-for-syntax
>   (define-syntax-class (element-exp unit)
> (pattern (element value)
>   #:with pred-name (format-id #'element "~a-~a?" unit #'element)
>   #:with variable #'(define element value)
>   #:with predicate #'(define (pred-name v) (eq? element v)
>
> (define-syntax (define-enum stx)
>   (syntax-parse stx
> [(_ name (~var e (element-exp #'name)) ...)
>  #'(begin
>  e.variable ...
>  e.predicate ...)]))
>
> It's a bit more complicated at first, but it becomes really powerful once you 
> understand how syntax-parse and syntax-classes work.
>
> (the tricky part here is that I'm using a syntax-class that takes the unit 
> name as an argument, which is not so common)
>
> Have fun ;)
>
>
> --
> 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.



-- 
Abraço,
Wanderley Guimarães

-- 
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] Re: I wrote my first macro ever!

2019-04-29 Thread Jérôme Martin
Hello there!

I'm really glad you're picking up Racket and enjoying it!

I've been through the same paths and struggles before, so thank you very 
much for posting that piece of
experience here :)

For your information, here is a syntax-parse version of your macro, which I 
find simpler to write and understand:
(it's basically syntactic sugar over syntax-case, but it's really useful!)

#lang racket/base

(provide
  define-enum)

(require
  (for-syntax
racket/base
racket/syntax
syntax/parse))

(define-syntax (define-enum stx)
  (syntax-parse stx
[(_ name (element value) ...)
 #:with (name-element? ...)
(datum->syntax
  #'(element ...)
  (map (lambda (elem)
 (format-id elem "~a-~a?" #'name elem))
   (syntax->list #'(element ...
 #'(begin
 (define element value) ...
 (define (name-element? v)
   (eq? element v)) ...)]))

(module+ test
  (require rackunit)
  
  (define-enum unit
(dry #\d)
(empty #\.)
(rock #\#)
(water #\~))
  
  (check-true (unit-dry? #\d))
  (check-false (unit-empty? #\#))
  (check-false (unit-rock? #\~))
  (check-true (unit-water? #\~)))

To remove that ugly map, which requires transforming syntaxes to list then 
back to syntaxes, you can use a syntax-class:

(begin-for-syntax
  (define-syntax-class (element-exp unit)
(pattern (element value)
  #:with pred-name (format-id #'element "~a-~a?" unit #'element)
  #:with variable #'(define element value)
  #:with predicate #'(define (pred-name v) (eq? element v)

(define-syntax (define-enum stx)
  (syntax-parse stx
[(_ name (~var e (element-exp #'name)) ...)
 #'(begin
 e.variable ...
 e.predicate ...)]))

It's a bit more complicated at first, but it becomes really powerful once 
you understand how syntax-parse and syntax-classes work.

(the tricky part here is that I'm using a syntax-class that takes the unit 
name as an argument, which is not so common)

Have fun ;)


-- 
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.