Sorry for my newbie metaprogramming question. Say I have this macro:
#lang racket/base
(require (for-syntax racket/base))
;; Depends on map, eval, quote
(define-syntax (eval-emit stx)
(syntax-case stx ()
[(_ expr ...)
(with-syntax
([exprs
(cons 'quote (list (syntax->list #'(expr ...))))])
#'(map eval exprs))]))
Which is trying to evaluate like a baby REPL. And I can:
> (eval-emit
(define x 2)
(define y 3)
(+ x y)
(* x y)
(eval-emit
(define z 5)
(+ x y z)))
;; => '(void void 5 6 (void 10))
in the REPL (ignore the namespace issue). But if I try to do something nasty:
> (define map 1)
> (eval-emit 1 2)
;; => application: not a procedure;
The macro is not hygienic anymore. On the other hand:
> (define if 1)
> (and 1 2 3)
;; => 3
The built-in macro works fine. How do I achieve the same thing? Or is eval-emit
designed wrongly from the very start and there exist better ways to do it?
Correct me if I'm wrong. I think what you're saying is that you want the map
that's inserted by your macro to refer to the value for map that was in existence when the macro was defined. I claim that this is not a hygiene problem; the map
inserted by your macro has the same binding as the one whose value you are changing later.
I also think that the problem you're seeing will disappear when you move to more substantial examples, where for instance the macro definition appears in a different module than the macro use.
... Instead, you'll have a whole bunch of different problems :).
I think this is where I point you to Matthew Flatt's blog post about eval, and how actually you really didn't want to use eval at all.
Or maybe you should read the one about the top level being hopeless?
More generally, if you want to use eval
in a way that doesn't wind up biting you in a nasty way later, you probably want to construct a complete module, and then use eval on that.
Let me return to the TL/DR: why is it that you want to use eval?
The final product I am working on should be something like this:
;; Say we have (define/expr name expr) that goes to
;; (begin
;; (define name expr)
;; name)
(foo
(define/expr a (bar 'stub 'stub))
(baz a 'stub))
Where foo
act like a function call with arbritay length arguments but inside it new names can be bind to and referenced to.
Also I'm happy to read about Flatt's blog. Where can I find them?
Does this do what you want?
#lang racket
(define-syntax (define/expr stx)
(syntax-case stx ()
[(_ name expr)
#'(begin (define name expr) name)]))
(define/expr a 14)
a
?
Hmm... or, based on your use case, it sounds like perhaps you want the name to be bound at the top level, as well? In that case, I think I personally would look into a whole-module transformation that inserts stub definitions at the top level, and then replaces uses of define/expr with uses of set!
. This could potentially be some kind of tricky re-binding-app-and-every-other-form to transitively hoist the definition out, but I'd be inclined to just do it as a whole-module transformation.
'
There may very well be a much more elegant way to do it than the just-traverse-the-module approach that I'm describing.
Indeed your define/expr
works but (to further clarify) I'm having trouble with foo
which will utilize eval-emit
to accomplish its functionality. i.e. Having a baby REPL inside it to keep track of side-effects (bindings) and get what expressions eval into.
And also thanks for your support <3
Are you intended to define both the macro and the new map
variable in the repl? I can't reproduce this error by defining the macro in a module and importing it into the repl.
If you mean to do so, then the question might be how an identifier refers to a particular binding.
According to the racket reference,
An identifier refers to a particular binding when the reference’s symbol and the identifier’s symbol are the same, and when the reference’s scope set is a superset of the binding’s scope set. For a given identifier, multiple bindings may have scope sets that are subsets of the identifier’s; in that case, the identifier refers to the binding whose set is a superset of all others; if no such binding exists, the reference is ambiguous (and triggers a syntax error if it is parsed as an expression). A binding shadows any binding (i.e., it is shadowing any binding) with the same symbol but a subset of scopes.
That is to say, the original map
binding is shallowed.
I clicked the Run
button in DrRacket and used the REPL there.
It turns out that DrRacket Run
is not equivalent to require
a file. Ouch. Problem solved.
But on a side note is my approach "idomatic"? I don't feel good using eval
.
As I understand it, eval
is typically used with namespaces. If you don't use customized namespaces, you can write a small DSL that recognizes and handles statements, expressions, etc.