[racket] an "outer" macro toy

From: Danny Yoo (dyoo at cs.wpi.edu)
Date: Tue Apr 3 15:24:46 EDT 2012

Thought this might amuse other folks; here is a sketch of what it can
look like to intentionally break lexical scoping in a controlled way.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#lang racket

;; Here's an implementation of an "outer" form that allows one to get
at the binding
;; outside of a current function definition.  For this to work, we
need special cooperation
;; from the function-building syntax.

(require racket/stxparam)

;; We keep track of the currently-expanding function.
(define-syntax-parameter current-scoped-fun #f)

;; The function form "fun" sets up a compile-time parameter that holds onto the
;; function.
(define-syntax (fun stx)
  (syntax-case stx ()
    [(_ ids body ...)
     (with-syntax ([fun-stx stx])
       (syntax/loc stx
         (syntax-parameterize ([current-scoped-fun #'fun-stx])
                              (lambda ids body ...))))]))

;; Finally, the "outer" form can use the current-scoped-fun to
;; create a new syntax object that takes its lexical information from
;; the entry of the function.
(define-syntax (outer stx)
  (syntax-case stx ()
    [(_ id)
     (identifier? #'id)
     (datum->syntax (syntax-parameter-value #'current-scoped-fun)
                    (syntax-e #'id)
                    stx)]))

;; For example:
(define x 42)
(define g (fun (x)
               (displayln x)
               (displayln (outer x))))

;; When we call:

(g 17)

;; we'll see the normal lexical binding on the first line (17),
;; followed by the value of the outer binding on the second line (42).
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



To make "outer" work more like a declaration, where it affects the
rest of the scope of a function body, we can use an internal
define-syntax, and then things look even more hilarious:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#lang racket

;; Second toy "outer":
(require racket/stxparam)

(define-syntax-parameter current-scoped-fun #f)

(define-syntax (fun stx)
  (syntax-case stx ()
    [(_ ids body ...)
     (with-syntax ([fun-stx stx])
       (syntax/loc stx
         (syntax-parameterize ([current-scoped-fun #'fun-stx])
                              (lambda ids body ...))))]))

(define-syntax (outer stx)
  (syntax-case stx ()
    [(_ id)
     (identifier? #'id)
     (with-syntax ([outered-id
                    (datum->syntax (syntax-parameter-value #'current-scoped-fun)
                                   (syntax-e #'id)
                                   stx)])
       #'(define-syntax id
           (make-rename-transformer #'outered-id)))]))

;; For example:
(define x 42)
(define g (fun ()
               (let ([x 16])
                 (displayln x)
                 (let ()
                   (outer x)
                   (displayln x))
                 (displayln x))))
(g)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


Anyway, just wanted to throw that out, since it helped me to
understand the role of the first argument to datum->syntax.

Posted on the users mailing list.