Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

Macros in Racket [Scheme][newbie][help]

Name: Anonymous 2011-02-28 20:17

Hi /prog/. So I've only just picked up Racket and I want to write some macros to use with an SQL database.

I'd like:
(wrapper wrapper-id
  [query-id "SQL"]
  [query-id "SQL" f ...])

to define a procedure make-wrapper-wrapper-id, which I would then use to create some value which would contain the prepared sql statements, and lambdas to apply to the results of the queries.

Then after doing
(define some-wrapper (make-wrapper-... ...)),
I'd like to say:
(some-wrapper :: query-id param ...)
to automatically invoke the statement associated with query-id and apply the stored lambda to the result.

My implementation (prepare and execute provided by the DB module):
(define-syntax mk-f
  (syntax-rules ()
    [(mk-f) (lambda (%) %)]
    [(mk-f f ...) (lambda (%) (f ... %))]))

(define-for-syntax (mk-spec form)
  (let* ([def (syntax->list form)])
    (with-syntax ([id (car def)]
                  [sql (cadr def)]
                  [(f ...) (cddr def)])
      #'(list* 'id sql (mk-f f ...)))))

(define-for-syntax (mk-c-id id)
  (string->symbol (string-append "make-wrapper-" (symbol->string (syntax->datum id)))))

(define-syntax (wrapper stx)
  (syntax-case stx ()
    [(wrapper id defs ...)
     (with-syntax ([c-id (mk-c-id #'id)]
                   [(spec ...) (map mk-spec (syntax->list #'(defs ...)))])
       #'(define (c-id connect)
           (make-wrapper connect (list spec ...))))]))

(define (make-wrapper connect specs)
  (let* ([conn (connect)]
         [f (lambda (x) (list (car x) (cons (prepare #:connection conn (cadr x)) (cddr x))))])
    (cons conn (hash (append-map f specs)))))

(define-syntax ::
  (syntax-id-rules ()
    [(w :: id param ...)
     (exec-query w (quote id) param ...)]))

(define (exec-query wrapper id . params)
  (let ([m (hash-ref (cdr wrapper) id)])
    ((cdr m) (execute (car m) params))))

But, well, it doesn't work. I've stepped through the (wrapper ...) macro in DrRacket and it expands just right, but it's like the define does nothing; I can't reference make-wrapper-wrapper-id at all! And using the :: macro causes a bad syntax error.
It's rather late and I'd really appreciate your help, guys!

Name: Anonymous 2011-02-28 21:50

If you add in a second rule [m #t], you will see that your pattern doesn't match. This is because identifier syntax (I'm specifically thinking of R6RS MAKE-VARIABLE-TRANSFORMER but this seems to be roughly the same) isn't supposed to be used for macros in an arbitrary position, but for 3 specific use cases.
1. Keyword on it's own
2. Keyword as the CAR of an expression
3. Keyword as the CADR of a SET! expression

Here's a handy guide

(define-syntax cocks
  (make-variable-transformer
   (lambda (stx)
     (syntax-case stx (set! omg-an-id)
       [(set! foo bar)
        #''set!]
       [(cocks omg-an-id)
        #''omg-an-id]
       [(cocks not-an-id)
        #''not-an-id]
       [cocks
        (identifier? #'cocks)
        #''all-by-myself]))))

(list (set! cocks foo)
      (cocks omg-an-id)
      (cocks are-so-fucking-cock-like)
      cocks)
;; will expand to
(list 'set! 'omg-an-id 'not-an-id 'all-by-myself)
;; although you will need to change a setting in Racket's expander to get the first one as it won't expand SET! by default
;; or you can check it with Guile's or Ikarus' or whatever


In general though, I stay clear of identifier syntax as it seems needlessly obfuscatory.

Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List