michaelrauh
2020-12-26 19:23:59

I have a question related to variable scoping during macro expansion. I posted something briefly earlier today but noticed that the question was not well stated and deleted it. If anyone would be willing to take a look, I would appreciate it. I’m putting some code to illustrate the question in a thread on this comment.


michaelrauh
2020-12-26 19:24:58

Essentially I am trying to rebind a variable in a loop to make a loop iterator. The code that exposes the issue looks like this:

(define-syntax-rule (loop id-sequence term-clause statement) (λ (input-list) (for/fold ([l input-list]) ([index (range 0 (length input-list) (length (cdr 'id-sequence)))]) (begin (define op 1) (statement l))))) Where statement is meant to have scope on op.

Here is an example statement: (statement (pointer-assignment op 4))

It seems clear that the reason this is not working is because of macro hygiene. I feel like the usual answer related to getting around these types of issues involves using syntax parameters, but I can’t seem to get that to work without defining the parameter in a hard coded way at the module level, but I do not know the identifier name until macro expansion time so that does not seem like it will work.


michaelrauh
2020-12-26 20:17:45

For anyone happening upon this, I think I have figured out that the answer is going to be unpacking the statement and repacking it into syntax with the required identifiers in scope. I have not implemented this yet but I think this is one of the use cases where tightly controlling the context makes sense. Implementation is hanging me up a bit so if anyone wants to lend a hand there, let me know!


michaelrauh
2020-12-26 22:39:55

As a small working example, I’ve proven out at least this much:

(define-syntax-rule (program subprogram) subprogram) (define-syntax (statement stx) #`(let ([op 3]) #,(car (cdr (syntax->datum stx))))) (program (statement (+ 1 op))) > 4 The hard part will be cleanly doing it inside of a for/fold and making sure that it is not too eager (as the index is a pointer into the accumulator which can change inside the loop)


sorawee
2020-12-26 22:46:39

Can you give a full example how you expect your macro to be used?


michaelrauh
2020-12-26 22:57:34

Certainly! The full file (with example input syntax at the bottom) looks like:

(provide #%module-begin) (require threading) (require "../util.rkt") (provide read) (provide delimiter) (require lens) (define-syntax-rule (tape-program read statement ...) (for/fold ([l read]) ([transform (list statement ...)]) (transform l))) (provide tape-program) (define-syntax-rule (statement substatement) substatement) (provide statement) (define (pointer-assignment target-pos new-val) (λ (l) (lens-set (list-ref-lens target-pos) l new-val))) (provide pointer-assignment) (define-syntax-rule (loop id-sequence term-clause statement) (λ (input-list) (for/fold ([l input-list]) ([index (range 0 (length input-list) (length (cdr 'id-sequence)))]) (begin (define op 1) (statement l))))) (provide loop) (tape-program (read 2 (delimiter "comma")) (statement (pointer-assignment 1 12)) (statement (pointer-assignment 2 2)) (statement (loop (identifier-sequence (identifier op) (identifier foo) (identifier bar)) (termination-clause op "=" 99) (statement (pointer-assignment op 4))))) The idea is that each statement writes back to a tape. Loops are statements that run substatements over and over again during tape traversal. The tape may change during this traversal.


michaelrauh
2020-12-26 23:00:41

So I think the solution will be to unpack the identifier sequence and bind each one to an offset of the iterator inside of the for/fold. Either way, I think this will be something to sort of chug on for awhile.


aymano.osman
2020-12-26 23:15:22

Is there a way to have rackunit render picts inside the “check-info stack”?


joshibharathiramana
2020-12-27 03:42:21

what’s the point of having separate let and letrec? Why not have just letrec since it can do everything let can (and more)?


samth
2020-12-27 04:06:19

If you shadow a variable using let, changing that to letrec won’t work.


samth
2020-12-27 04:06:48

Also, using less powerful constructs can make things easier to understand


joshibharathiramana
2020-12-27 04:11:00

@samth this seems to work, I don’t understand what you mean "letrec.rkt"> (letrec ([x 5]) (letrec ([x 6]) x)) 6