[racket] Macros and dynamically generating identifiers

From: Jay McCarthy (jay.mccarthy at gmail.com)
Date: Thu May 2 22:35:08 EDT 2013

You were close to what you want. Here's a version with a nice utility
and then the underlying machinery that makes it:

#lang racket
(require (for-syntax racket/syntax))

(define-syntax (double-define stx)
  (syntax-case stx (double-define)
    [(_ id val1 val2)
     (with-syntax ([id-1 (format-id #'id "~a-1" #'id)]
                   [id-2 (datum->syntax
                           (format "~a-2"
       #'(begin (define id-1 val1)
                (define id-2 val2)))]))

(double-define id 3 7)
(displayln id-1)
(displayln id-2)

On Thu, May 2, 2013 at 8:29 PM, Sean McBeth <sean.mcbeth at gmail.com> wrote:
> Hi there!
> I'm pretty new to Racket, though not the basic concepts of functional
> programming [1] Maybe I don't need macros here at all, but it seemed like
> the right sort of lever when I first started, but now I'm pretty stuck[2]
> and I don't understand enough about the macro system yet to be able to
> figure this out.
> Basically, I'm trying to make a database migration tool + relational mapper.
> I'd like to be able to define my tables in an abbreviated Racket syntax and
> use the definition to generate everything from the create-table SQL scripts,
> a few, basic CRUD-scripts-for-all-columns to structs that will mirror a full
> table row when processing the query results.
> Right now, the table definition looks like this:
> (define-table tickets get-all-tickets
>  ([ticket_id serial primary-key]
>   [priority int nullable] ;; I believe in making not-null the default case
>   [description (varchar max)]
>   [finished_on datetime (default "9999-12-31 23:59:59.999")])
> And this is pretty easy to parse into some "table" structs that describe
> everything fairly sufficiently[3]:
> https://gist.github.com/capnmidnight/5506674
> Now, my sticking point is that I don't want to have explicitly define that
> "get-all-tickets" identifier. I notice that, in my creating the "column"
> struct, I've received a number of procedures for the constructor and field
> accessors, all given a prefix of "column" for their identifier. So at first
> glance, it seems like there are forms like struct that are capable of
> dynamically defining identifiers.
> So, I stepped into the definition for struct and tried to make sense of it,
> but the best I could figure out was that struct used syntax-case instead of
> syntax-rules. It was a bit of a hair-ball for me, I couldn't suss out the
> cross references, and at least at this late of an hour I'm having trouble
> understanding the documentation on syntax-case.
> Specifically, I tried to do something like:
> (define-syntax (double-define stx)
>   (syntax-case stx (double-define)
>     [(_ id val1 val2)
>      #`(begin (define id-1 val1)
>               (define id-2 val2))]))
> (double-define id 3 7)
> (displayln id-1) ;; error "id-1 unbound identifier"
> (displayln id-2)
> I then tried something like:
> (define-syntax (double-define stx)
>   (syntax-case stx (double-define)
>     [(_ id val1 val2)
>      (with-syntax ([id-1 #'(string->symbol (format "~a-1" id))] ;; error
> "define: not an identifier, identifier with default, or keyword for
> procedure argument"
>                    [id-2 #'(string->symbol (format "~a-2" id))])
>        #'(begin (define id-1 val1)
>                 (define id-2 val2)))]))
> (double-define id 3 7)
> (displayln id-1)
> (displayln id-2)
> Clearly, not correct.
> I could make the table struct into a table class and then just define a
> get-all method that does what I want, but that kind of feels like giving up
> and I'm more interested in using this to learn more about using macros, as
> it has already done for me.
> [1] Functional C# is something of a job safety program of mine :P
> [2] i.e. been banging my head against the desk for the last 6 hours. I have
> gotten pretty comfortable with syntax-rules though, so it wasn't a complete
> waste.
> [3] This isn't the final form, but I'm just pushing some code around to try
> to get the basic concepts working. For example, the get-all-tickets
> procedure wouldn't just return the query, it'd eventually execute it and
> return the results.
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users

Jay McCarthy <jay at cs.byu.edu>
Assistant Professor / Brigham Young University

"The glory of God is Intelligence" - D&C 93

Posted on the users mailing list.