Introduce bindings for macro user I'm doing the Racket track on exercism.io and solved the grains exercise:


  Write a program that calculates the number of grains of wheat on a chessboard given that the number on each square doubles.


(test cases etc on exercism)

So I wrote a straightforward recursive solution:

#lang racket

(provide square total)

(define (square n)
  (cond
    [(n . = . 1)    1]
    [(n . &gt; . 1)    (* 2 (square (- n 1)))]
    [else           (error "invalid number:" n)]))

(define (sum n)
  (cond
    [(n . = . 1)    1]
    [(n . &gt; . 1)    (+ (square n) (sum (- n 1)))]
    [else           (error "invalid number:" n)]))

(define (total)
  (sum 64))


Then I went to clean it up by factoring out the induction:

#lang racket

(provide square total)

(define-syntax (induction stx)
  (syntax-case stx ()
    ((induction base rule)
     (with-syntax ((n (datum-&gt;syntax stx 'n)))
       #'(lambda (n)
           (cond
             ((n . = . 1)   base)
             ((n . &gt; . 1)   rule)
             (else          (error "invalid number:" n))))))))

(define square  (induction 1 (* 2 (square (- n 1)))))
(define sum     (induction 1 (+ (square n) (sum (- n 1)))))

(define (total)
  (sum 64))


I got a couple of questions:

1) Is that the most straightforward way to introduce a new binding for the macro user?

Like, this is quite wordy. I guess I could wrap the "with-syntax" part into another macro, 'cause right now this feels very "built out of raw plumbing" to me.

It's also annoying that "define-syntax-rule" doesn't give me "stx", so I don't see a way to introduce new bindings. I'd like to write something like:

(define-syntax-rule (induction base rule)
  (syntax-let ((n))
    (lambda (n)
      (cond
        ((n . = . 1)    base)
        ((n . &gt; . 1)    rule)
        (else           (error "invalid number:"))))))


Is there anything built-in for that?

2) For argument clarity, I'd like to write it more like this:

(define (square n)
  (induction 1 (* 2 (square (- n 1)))))


How'd I do that? Like, I'd... have to capture the "n" argument correctly (how?), and return, uh... the body of the lambda? I'm confused.
When writing macros, it is best to avoid using functions like datum-&gt;syntax to conjure up identifiers. Instead, your macro should take the identifiers to bind from the macro use site.

For your example, I would write it like this:

#lang racket

(provide square total)

;; note how `n` is passed in as an argument
(define-syntax-rule (induction n base rule)
  (lambda (n)
    (cond [(n . = . 1) base]
          [(n . &gt; . 1) rule]
          [else        (error "invalid number:" n)])))

(define square (induction n 1 (* 2 (square (- n 1)))))
(define sum    (induction n 1 (+ (square n) (sum (- n 1)))))

(define (total)
  (sum 64))


Macros written in this style are more composable and cooperate with macro hygiene. Racket's macro system lets you break this hygiene in a controlled way, but that is usually a bad idea.

Also, for a simple program like this I would usually not bother writing a macro. In this case, you could even have abstracted this using only first-class functions instead of macros.

You may also wish to read Macros and Languages in Racket and Fear of Macros if you haven't already.