# Wrap a macro injecting bindings in another macro

**URL:** https://racket.discourse.group/t/wrap-a-macro-injecting-bindings-in-another-macro/1406
**Category:** Questions & Answers
**Tags:** macro
**Created:** [October 19, 2022, 2:43pm UTC](https://racket.discourse.group/t/wrap-a-macro-injecting-bindings-in-another-macro/1406 "2022-10-19T14:43:46Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)
#### Post date: [October 19, 2022, 2:43pm UTC](https://racket.discourse.group/t/wrap-a-macro-injecting-bindings-in-another-macro/1406/1 "2022-10-19T14:43:46Z")

</div>

Hi,

I have in my toolbox the macro `auto-hash-ref/:`, defined [here](https://git.marvid.fr/scolobb/dds/src/branch/master/utils.rkt#L123). Essentially, the idea is to automatically search for a variable value in a hash table. For example:

```scheme
(define ht (hash 'a 1 'b 2))
(auto-hash-ref/: ht (+ :a (* 2 :b)))

```

The second line of this example expands to the following:

```scheme
(let ([:a (hash-ref ht 'a)]
      [:b (hash-ref ht 'b)])
  (+ :a (* 2 :b)))

```

Now I would like to wrap `auto-hash-ref/:` in another macro to make function definition easier. I write the following code (everything is in Typed Racket):

```scheme
(define-syntax-parse-rule (lambda/: (ht:id type:expr) body:expr)
  (lambda ([ht : type])
    (auto-hash-ref/: ht body)))

```

I try to use this macro in the following way:

```scheme
(lambda/: (st (HashTable Symbol Integer)) (+ :a :b))

```

and I get the error (trimmed):

```scheme
; :a: unbound identifier
; in: :a

```

I compared the final expansion (obtained via Racket Mode's macro stepper) of `auto-hash-ref/:` and `lambda/:`. For `(auto-hash-ref/: s (+ :a :b))` I obtain

```scheme
(let-values (((:a) (#%app hash-ref (#%top . s) 'a))
             ((:b) (#%app hash-ref (#%top . s) 'b)))
  (#%app + :a :b))

```

For `(lambda/: (st (HashTable Symbol Integer)) (+ :a :b))` I obtain

```scheme
(#%expression
 (lambda (st)
   (let-values (((:a) (#%app hash-ref st 'a)) ((:b) (#%app hash-ref st 'b)))
     (#%app + (#%top . :a) (#%top . :b)))))

```

I remark that `:a` and `:b` in the second expansion are wrapped in `#%top`, which [I think means](https://docs.racket-lang.org/reference/__top.html#%28form._%28%28quote._~23~25kernel%29._~23~25top%29%29) that Racket looks for bindings for `:a` and `:b` in the wrong place.

How should I correctly write the macro `lambda/:`?

---

<div class="post-metadata">

### Author: ![samth](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/samth/32/3_2.png) [@samth](https://racket.discourse.group/u/samth)
#### Post date: [October 19, 2022, 4:22pm UTC](https://racket.discourse.group/t/wrap-a-macro-injecting-bindings-in-another-macro/1406/2 "2022-10-19T16:22:41Z")

</div>

This is a classic program in the uncomposability of unhygenic macros. (This is discussed some in the syntax-parameters paper.) Basically, the interface of `auto-hash-ref/:` has as part of its interface the syntax of the whole form, which is where you get the syntactic context for all the variable bindings you insert. Then your `lambda/:` macro creates that syntax in its body, so that the bindings of :a and :b have the syntactic contexts of the expansion of `lambda/:` instead of its input.

The best solution is to (a) make it clear in the description of `auto-hash-ref/:` where it takes syntactic context from (b) choose something other than the whole form (such as the name of the hash table) and (c) ensure that the syntactic context of that comes from the input (which it does for that choice in your `lambda/:` macro). Basically you would change `stx` to `#'id` in the first argument to `datum->syntax` [here](https://git.marvid.fr/scolobb/dds/src/branch/master/utils.rkt#L129).

Unfortunately, even if you follow those rules, you will often need to write unhygienic macros to abstract over `auto-hash-ref/:`. For example, if you want to write this macro:

```scheme
(define-syntax-rule (auto-lambda body) (lambda (i) (auto-hash-ref/: i body)))

```

there is no way to write it as a hygienic macro and have it do what you want. Instead, even with the change I suggest, you would write:

```scheme
(define-syntax (auto-lambda stx)
   (syntax-parse stx
     [(_ b:expr)
      #:with i (format-id #'b "i")
      (lambda (i) (auto-hash-ref/: i body))]))

```

---

<div class="post-metadata">

### Author: ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)
#### Post date: [November 1, 2022, 9:33pm UTC](https://racket.discourse.group/t/wrap-a-macro-injecting-bindings-in-another-macro/1406/3 "2022-11-01T21:33:16Z")

</div>

Thank you very much @samth for your detailed explanations and suggestions! It turns out that the `auto-lambda` macro from your answer better corresponds to my use case.

I took some time to read and think over your message, and I finally start understanding why `datum->syntax`, `format-id`, etc. take a syntax context as an argument 😃

I guess am ready for going deeper on hygienic macros, so if anyone has references to suggest, please shoot.

---

<div class="post-metadata">

### Author: ![samth](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/samth/32/3_2.png) [@samth](https://racket.discourse.group/u/samth)
#### Post date: [November 2, 2022, 5:10pm UTC](https://racket.discourse.group/t/wrap-a-macro-injecting-bindings-in-another-macro/1406/4 "2022-11-02T17:10:07Z")

</div>

The best introduction is [Fear of Macros](https://www.greghendershott.com/fear-of-macros/) by @greghendershott.

---

<div class="post-metadata">

### Author: ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)
#### Post date: [November 2, 2022, 5:49pm UTC](https://racket.discourse.group/t/wrap-a-macro-injecting-bindings-in-another-macro/1406/5 "2022-11-02T17:49:03Z")

</div>

Thanks, reading that document one more time will definitely do me good.
