# Let over lambda and source location information

**URL:** <https://racket.discourse.group/t/let-over-lambda-and-source-location-information/306>\
**Category:** General\
**Created:** [November 29, 2021, 8:49pm UTC](https://racket.discourse.group/t/let-over-lambda-and-source-location-information/306 "2021-11-29T20:49:41Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![cwebber](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/cwebber/32/199_2.png) [@cwebber](https://racket.discourse.group/u/cwebber)\
**Post date:** [November 29, 2021, 8:49pm UTC](https://racket.discourse.group/t/let-over-lambda-and-source-location-information/306/1 "2021-11-29T20:49:41Z")

</div>

Take the following code:

```scheme
(require racket/match)

(define-syntax-rule (methods* [(method-name method-args ...) body ...] ...
                              fallback)
  (let ((method-name
         (lambda (method-args ...)
           body ...)) ...)
    (define all-methods (list (cons 'method-name method-name) ...))
    (define method-dispatch
      (make-keyword-procedure
       (lambda (kw-args kw-vals method . args)
         (match (assq method all-methods)
           [(cons _name found-method)
            found-method
            #;(keyword-apply found-method kw-args kw-vals args)]
           [#f
            (keyword-apply fallback kw-args kw-vals method args)]))))
    method-dispatch))

(define no-such-method
  (make-keyword-procedure
   (lambda (kw-vals kw-args method . args)
     (error "No such method" method))))

(define-syntax-rule (methods method-defns ...)
  (methods* method-defns ... no-such-method))

```

This is kind of a kluge, I know. But you get the idea. Let over  
lambda, because we're going to be reusing these procedures over and over  
again across multiple calls.

Now let's say I instantiate this like:

```scheme
  (define my-methods
    (methods
     [(double x)
      (* x x)]))

```

```scheme
> my-methods
#<procedure:...tor-lib/methods.rkt:130:7>

```

That's the line where method-dispatch is defined, _inside the macro_.  
But what I really want is for the annotation on the procedure to be  
_where my-methods is defined_.... not pointing back inside the macro.

I have no idea how to do this. Thoughts?

---

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [November 29, 2021, 9:30pm UTC](https://racket.discourse.group/t/let-over-lambda-and-source-location-information/306/2 "2021-11-29T21:30:03Z")

</div>

In the macro template you have:

```
(lambda (kw-args kw-vals method . args) ...)

```

The default source location information thus points to the definition inside the macro.  
To get a new location one option is to use `syntax/loc`:

```
(syntax/loc #'loc-stx    
  (lambda (kw-args kw-vals method . args) ...))

```

where `loc-stx` is a syntax object whose source location is the location, you want to  
give to the lambda expression.

In your macro that location comes from the application of `method`. That means `method` needs to send the location along to `method*`. One of way doing so is shown below.

```scheme
#lang racket
(require racket/match
         (for-syntax syntax/parse))

(define-syntax (methods* stx)
  (syntax-parse stx
    [(methods* loc-stx
               [(method-name method-args ...) body ...] ...
               fallback)
     #`(let ((method-name
              (lambda (method-args ...)
                body ...)) ...)
         (define all-methods (list (cons 'method-name method-name) ...))
         (define method-dispatch
           (make-keyword-procedure
            #,(syntax/loc #'loc-stx       
                (lambda (kw-args kw-vals method . args)
                  (match (assq method all-methods)
                    [(cons _name found-method)
                     found-method
                     #;(keyword-apply found-method kw-args kw-vals args)]
                    [#f
                 (keyword-apply fallback kw-args kw-vals method args)])))))
         method-dispatch)]))
  
(define no-such-method
  (make-keyword-procedure
   (lambda (kw-vals kw-args method . args)
     (error "No such method" method))))

(define-syntax (methods stx)
  (syntax-parse stx
    [(_methods method-defns ...)
     #'(methods* stx method-defns ... no-such-method)]))

(define my-methods
  (methods
   [(double x)
    (* x x)]))

my-methods

```

If you want both `mehod*` and `method` to be available to the user, then adding an argument to `method*` might not be desirable. In that case, rename `method*` above to, say, `do-method*` and define two macros `method` and `method*` which both expands to `do-method*`.

---

<div class="post-metadata">

**Author:** ![cwebber](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/cwebber/32/199_2.png) [@cwebber](https://racket.discourse.group/u/cwebber)\
**Post date:** [November 29, 2021, 10:03pm UTC](https://racket.discourse.group/t/let-over-lambda-and-source-location-information/306/3 "2021-11-29T22:03:44Z")

</div>

This is great! Thanks!

---

<div class="post-metadata">

**Author:** ![sorawee](https://avatars.discourse-cdn.com/v4/letter/s/ea5d25/32.png) [@sorawee](https://racket.discourse.group/u/sorawee)\
**Post date:** [December 6, 2021, 3:47pm UTC](https://racket.discourse.group/t/let-over-lambda-and-source-location-information/306/4 "2021-12-06T15:47:04Z")

</div>

@soegaard's code has a typo, I think? `methods` should look like this for the srcloc to propagate correctly:

```scheme
(define-syntax (methods stx)
  (syntax-parse stx
    [(_methods method-defns ...)
     #`(methods* #,stx method-defns ... no-such-method)]))

```

FWIW, I gave a similar solution, along with another solution that uses `syntax-local-name` in the [mailing list](https://groups.google.com/g/racket-users/c/IjSTVt9Vds4/m/kiEhY2lRBgAJ), which, with high probability, probably went to the spam folder.
