Skip to content

Instantly share code, notes, and snippets.

@tfeb
Last active August 29, 2015 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tfeb/0b8531c94cf685824626 to your computer and use it in GitHub Desktop.
Save tfeb/0b8531c94cf685824626 to your computer and use it in GitHub Desktop.
Racket macro idiomaticity
#lang racket
(define (call/environment-variables thunk bindings)
;; This is a conventional call-with-whatever function: it calls thunk
;; with an environment augmented by bindings which is a list of pairs of
;; names and values
(let ([e (environment-variables-copy
(current-environment-variables))])
(for ([b bindings])
(match-let ([(list n v) b])
(environment-variables-set! e n v)))
(parameterize ([current-environment-variables e])
(thunk))))
;;; My original overcomplicated version
;;;
;(define-syntax (with-environment-variables stx)
; ;; This is the macro. The only significant thing here is that I want everything
; ;; to be evaluated: both names and values of variable (I am not sure why), so
; ;; (w-e-v ([n v] ...) ...) -> (call/ev (λ () ...) (list (list n v) ...))
; (syntax-case stx ()
; [(_ () body ...)
; #'(call/environment-variables (λ () body ...) '())]
; [(_ (binding ...) body ...)
; #`(call/environment-variables
; (λ () body ...)
; (list #,@(for/list ([b (syntax->list #'(binding ...))])
; (syntax-case b ()
; [(n v)
; #'(list n v)]))))]))
;;; bmastenbrook's pretty version
;;;
;(define-syntax with-environment-variables
; (syntax-rules ()
; [(_ ([name value] ...) body ...)
; (call/environment-variables
; (λ () body ...) (list (list name value) ...))]))
;;; My less-pretty but, I think, equivalent version
;;;
(define-syntax (with-environment-variables stx)
(syntax-case stx ()
[(_ ([name value] ...) body ...)
#'(call/environment-variables
(λ () body ...)
(list (list name value) ...))]))
(module+ test
(require rackunit)
(let ([n #"FOO"] [v #"BAR"] [n2 #"FISH"] [v2 #"BAT"])
(check-equal?
(call/environment-variables
(λ () (environment-variables-ref (current-environment-variables) n))
`([,n ,v]))
v)
(check-equal?
(with-environment-variables ([n v])
(environment-variables-ref (current-environment-variables) n))
v)
(check-equal?
(with-environment-variables ([n v] [n2 v2])
(let ([e (current-environment-variables)])
(map (λ (n) (environment-variables-ref e n)) `(,n ,n2))))
`(,v ,v2)))
(check-not-eq?
(with-environment-variables () (current-environment-variables))
(current-environment-variables)))
@bmastenbrook
Copy link

No problem; sometimes an example is worth a thousand documents. Your version is definitely equivalent to mine; the use of quasisyntax (the #` form) is the same as syntax (#') because you don't have any unquote forms, and syntax-rules is always trivially convertible to syntax-case:

(require (for-syntax (rename-in racket [syntax-rules racket:syntax-rules])))

(begin-for-syntax
  (define-syntax syntax-rules
    (racket:syntax-rules ()
      [(_ literals (pattern expansion) ...)
       (lambda (s)
         (syntax-case s literals
           (pattern #'expansion) ...))])))

Have you seen Greg Hendershott's "Fear Of Macros"? It might help if you're interested in continuing down the Racket macrology path: http://www.greghendershott.com/fear-of-macros/

@tfeb
Copy link
Author

tfeb commented Jan 13, 2015

Thanks: it was meant to be #': the backquote got in by mistake.

I should look at "Fear Of Macros": I was aware of it but had (probably incorrectly) assumed it was aimed at people who aren't used to the idea of Lisp-family macros at all, rather than people trying to grok Racket's in particular.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment