Set! global from macro schema?

I am trying to write a wrapper for define that stores the values ​​passed to it. I approached him in the children's steps (new to Lisp in general and even new to Scheme), but ran into a wall.

In Racket, I start with:

 > (require (lib "defmacro.ss")) > (define-macro (mydefine thing definition) `(define ,thing ,definition)) > (mydefine a 9) > a 9 

Ok, that works. Time to do something in the macro before s-exprs returns:

 > (define-macro (mydefine thing definition) (display "This works") `(define ,thing ,definition)) > (mydefine a "bob") This works > a "bob" 

Nice. But I can’t force him to set a global variable for life instead of displaying something:

 > (define *myglobal* null) > (define-macro (mydefine thing definition) (set! *myglobal* "This does not") `(define ,thing ,definition)) > (mydefine a ":-(") set!: cannot set identifier before its definition: *myglobal* 

Any suggestions on how to do this would be greatly appreciated.

I suspect that I am trying to swim against the current one here, either by confusing the globals from the macro in the Schema, or using define-macro instead of examining the syntax specific to the scheme for creating the macro.

+4
source share
2 answers

You work against phase separation Racket - this means that each phase (runtime and compilation time) work in different worlds. As Vijay notes, one way to solve this problem is to do what you want at runtime, but that probably won't be what you need in the long run. The fact is that trying these things usually means that you want to keep some syntax information at the compile-time level. For example, say you want to keep the names of all of your specific names that will be used in the second macro, which will print them all. You would do it as follows (I use reasonable macros here, define-macro is an outdated hack that cannot be used for real work, you can see these things in the guide , and then in the reference ):

 #lang racket (define-for-syntax defined-names '()) (define-syntax (mydefine stx) (syntax-case stx () [(_ name value) (identifier? #'name) (begin (set! defined-names (cons #'name defined-names)) #'(define name value))] ;; provide the same syntactic sugar that `define' does [(_ (name . args) . body) #'(mydefine name (lambda args . body))])) 

Note that defined-names is defined at the syntax level, which means that the normal runtime code cannot reference it. In fact, you can bind it to a different value at the run level, since the two bindings are different. Now that this is done, you can write a macro that uses it, even if defined-names not available at run time, this is a simple syntax-level binding, so:

 (define-syntax (show-definitions stx) (syntax-case stx () [(_) (with-syntax ([(name ...) (reverse defined-names)]) #'(begin (printf "The global values are:\n") (for ([sym (in-list '(name ...))] [val (in-list (list name ...))]) (printf " ~s = ~s\n" sym val))))])) 
+6
source

The statement (set! *myglobal* "This does not") is executed in a transformer environment, and not in a normal environment. Therefore, he could not find *myglobal . We need to get both expressions executed in an environment where *myglobal* defined.

Here is one solution:

 (define *defined-values* null) (define-macro (mydefine thing definition) `(begin (set! *defined-values* (cons ,definition *defined-values*)) (define ,thing ,`(car *defined-values*)))) > (mydefine a 10) > (mydefine b (+ 20 30)) > a 10 > b 50 > *defined-values* (50 10) > (define i 10) > (mydefine a (begin (set! i (add1 i)) i)) ;; makes sure that `definition` ;; is not evaluated twice. > a 11 

If the schema implementation does not provide define-macro but has define-syntax , mydefine can be defined as:

 (define-syntax mydefine (syntax-rules () ((_ thing definition) (begin (set! *defined-values* (cons definition *defined-values*)) (define thing (car *defined-values*)))))) 
+4
source

Source: https://habr.com/ru/post/1346178/


All Articles