[racket] Racket macro state reset

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Mon Feb 16 14:58:49 EST 2015

At Mon, 16 Feb 2015 14:36:16 -0500, "Paul Ojanen" wrote:
> I'm working on a macro that will have mutable state. 
> [I'm new to macros in general, and I'm new to pattern matching in Racket and
> all things related to syntax functions and objects.  I'm also still learning
> about macro expansion, compile time, run time, etc.]

In case you haven't looked at it already, this paper might be helpful:

 "Composable and Compilable Macros: You Want it When?"

> I was surprised that the macro's state is empty when I try to interact with
> it from the Interactions Window.  Are all the compile/macro-expansion time
> definitions redefined for the Interactions Window?

Yes. Each compilation of a module starts with fresh compile-time state
for any module that it imports, including a module's own state while
the module is being compiled. The top-level (for interaction) acts as a
kind of module for that purpose.

> In the below Interactions Window dialogue, why does (acc) start referring to
> a different hashtable than ht2?

The compile-time part of the module is instantiated once when then
module is compiled, and `(acc)` expands to a mutable hash table during
that compilation. In other words, the compiled form of the module has a
specific mutable hash table for the right-hand side of `ht2`, and it's
the same hash table every time the module is instantiated. Using
`(acc)` in the interactions window, however, gets you a hash table from
a fresh compile-time instance of the module.

The fact that `datum->syntax` accepts a mutable hash table is a hole
Racket's separation of phases. We sometimes call the result "3-D
syntax", because it creates a syntax object that can't be written as
literal syntax. We haven't closed the hole, for various reasons, but
you should avoid 3-D syntax for your sanity and so that various Racket
tools (such as `raco make`) can work.

As it turns out, constructing 3-D syntax with a mutable hash table can
be particularly confusing, because the hash table will get coerced to
an immutable hash table if the code is ever marshaled to bytes, such as
in a ".zo" file. So, it will seem to work for slightly longer than 3-D
syntax usually seems to work, but it really doesn't work.

> Here's my stripped-down code:
> #lang racket
> (begin-for-syntax
>   (define ht (make-hash))
>   )
> (define-syntax (acc stx)
>   (syntax-case stx ()
>     [(_ (f (fn x) body)) (begin
>                            ; Record what was defined within the acc block
>                            (hash-set! ht
>                                       (syntax->datum (syntax fn))
>                                       (syntax->datum (syntax body)))
>                            ; Define it in the real evironment
>                            #'(f (fn x) body))]
>     [(_) (datum->syntax #'acc ht)] ; see what's been recorded
>     ))
> ; Define a sqr function
> (acc
> (define (sqr2 x) (* x x))
> )
> ; Try the sqr function
> (sqr2 5)
> ; See what was recorded by the macro
> (acc)
> ; Bind a run-time variable to the macro's hashtable
> (define ht2 (acc))
> --------Interactions Window dialogue
> 25
> '#hash((sqr2 . (* x x)))
> > (sqr2 5)
> 25
> > (acc) ;the hashtable is empty
> '#hash()
> > ht2 ;no, it's not
> '#hash((sqr2 . (* x x)))
> > (acc (define (sqr3 x) (* x x)))
> > (acc)
> '#hash((sqr3 . (* x x)))
> > ht2
> '#hash((sqr2 . (* x x)))
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users

Posted on the users mailing list.