Hi,
I'm trying to write a macro that fully-expands define forms with the goal of doing some static checking (and thus want the code in racket core so that I know what I'm looking at). Ideally, this macro would work in any context where a define form works. Unfortunately, I'm having a great deal of difficulty using local-expand to get what I want. I've tried a few approaches---the code and simple examples are below.
Local expanding as a 'top-level' definition was closest to working because it both fully-expands the definition and inserts binding into the enclosing definition context. Unfortunately, it seems to fail to bind the identifier correctly in the body of its own define.
I tried expanding in the 'syntax-local-context', but this causes define-values to complain that it isn't in a definition context (even though syntax-local-context is a module context, which I thought was a definition context).
Creating a new definition context makes the expansion work, but the binding goes into the newly created context, which isn't what I want. It also doesn't fully expand the definition. (If I remove define-values from the stop list, it tries to fully-expand but dies complaining that define-values is not in a definition context).
I think I'm probably misreading the documentation. Can anyone suggest a correct solution?
Thanks,
Scott
#lang racket
(require (for-syntax syntax/parse
syntax/parse/lib/function-header))
(define-for-syntax (definition-local-context-expand stx)
(local-expand stx (syntax-local-context) '()))
(define-syntax (define-expand-top-level stx)
(syntax-parse stx
#:literals (define-expand-top-level)
[(define-expand-top-level header:function-header body ...+)
(local-expand #'(define header body ...) 'top-level '())]))
(define-syntax (define-expand-local-context stx)
(syntax-parse stx
#:literals (define-expand-local-context)
[(define-expand-local-context header:function-header body ...+)
(local-expand #'(define header body ...) (syntax-local-context) '())]))
(define-syntax (define-expand-new-context stx)
(syntax-parse stx
#:literals (define-expand-new-context)
[(define-expand-new-context header:function-header body ...+)
(let* ([def-ctx (syntax-local-make-definition-context)]
[ctx (if (list? (syntax-local-context))
(cons (gensym) (syntax-local-context))
(list (gensym)))]
[expd (local-expand #'(define header body ...) ctx (list #'define-values) def-ctx)])
(define ids (syntax-parse expd [(def (id) _) (list #'id)]))
(syntax-local-bind-syntaxes ids #f def-ctx)
(internal-definition-context-seal def-ctx)
expd)]))
; Works as expected
(define-expand-top-level (foo n)
(+ n 1))
(foo 5)
; Fails to bind fib in the body of the define
#;(define-expand-top-level (fib n)
(if (<= n 1)
1
(+ (fib (- n 1)) (fib (- n 2)))))
; fib: unbound identifier in module in: fib
; Attempt to use the local-context (which should be a definition context?)
#;(define-expand-local-context (fib n)
(if (<= n 1)
1
(+ (fib (- n 1)) (fib (- n 2)))))
; define-values: not in a definition context in:
; (define-values (fib) (lambda (n) (if (<= n 1) 1 (+ (fib (- n 1)) (fib (- n 2))))))
; Creating new definition context makes the expand work but doesn't expose
; the identifiers to the definition context I really want
(define-expand-new-context (fib n)
(if (<= n 1)
1
(+ (fib (- n 1)) (fib (- n 2)))))
#;(fib 3)
; fib: unbound identifier in module in: fib