syntax-objects / Summer2021

Syntax Parse Bee 2021
11 stars 3 forks source link

`define*` - A single form for making multiple definitions #7

Open Fictitious-Rotor opened 3 years ago

Fictitious-Rotor commented 3 years ago

Macro

Make a sequence of definitions - similar to let*.

(define-syntax-parser define*
  [(_ (~or [id:id expr:expr]
           [(idv:id ...+) expr:expr]) ...+)
   #'(begin
       (~? (define id expr)
           (define-values (idv ...) expr)) ...)])

Example

(define*
  [x 4]
  [y 18]
  [(quot rem) (quotient/remainder x y)])

quot rem
; -> 0
; -> 4

Definitions are automatically dispatched either to define or define-values depending on the manner in which identifiers are supplied (define-values is used if the identifiers are contained within a set of parenthesis)

Before and After

This macro saves a lot of unneccesary code when laying out a sequence of definitions

(define-values (pos1-x pos1-y) (values 14 32))
(define-values (pos2-x pos2-y) (values -24 5))
(define-values (x-diff y-diff) (values (- pos2-x pos1-x) (- pos2-y pos1-y)))
(define sqr (curryr expt 2))
(define distance (sqrt (+ (sqr x-diff) (sqr y-diff))))

;; Would become...

(define*
  [(pos1-x pos1-y) (values 14 32)]
  [(pos2-x pos2-y) (values -24 5)]
  [(x-diff y-diff) (values (- pos2-x pos1-x) (- pos2-y pos1-y))]
  [sqr (curryr expt 2)]
  [distance (sqrt (+ (sqr x-diff) (sqr y-diff)))])

;; Which is much less visually noisy

Inner workings

The macro works by accepting either type of clause (single or multiple values)

(~or [id:id expr:expr]
     [(idv:id ...+) expr:expr]) ...+

Resulting in a sequence of patterns, each of which bind either id or a list of idv - as well as expr The macro then uses the ~? fallthrough syntax to choose which syntax to produce in response.

(~? (define id expr)
    (define-values (idv ...) expr)) ...

If id is not bound then the syntax containing idv will be produced instead. The ellipsis syntax then repeats this for the whole list of patterns.

Previous iterations

I've unearthed an older version of this macro, which made use of recursive expansion

(define-syntax-parser define*
  [(_ (id:id expr:expr) next ...+)
   #'(begin
       (define id expr)
       (define* next ...))]
  [(_ (id:id expr:expr))
   #'(define id expr)])

Of course this macro does not support definitions that bind multiple identifiers at once - although it does handily demonstrate recursive macros.

Licence

Please confirm that you are submitting this code under the same MIT License that the Racket language uses. https://github.com/racket/racket/blob/master/racket/src/LICENSE-MIT.txt Please confirm that the associated text is licensed under the Creative Commons Attribution 4.0 International License http://creativecommons.org/licenses/by/4.0/

I confirm that the code is under the same license as the Racket language, and associated text is under Creative Commons Attribution 4.0 International License

Contact

I've already submit a macro, but I'd like to continue contributing to this resource!

spdegabrielle commented 3 years ago

Thank you for your contribution!

If you haven’t already please take the time to fill in the form https://forms.gle/Z5CN2xzK13dfkBnF7

Bw Stephen