chansey97
2022-3-2 13:28:54

Hi, I have a question about hygienic macro. Suppose I’d like to define an “advanced” or. (define (test? x) (>= x 0)) (define-syntax gor (syntax-rules () [(gor exp1 exp2) (let ((first-result exp1)) (if (test? first-result) first-result exp2))])) The result is as expected: (gor (* 1 -10) 42) ; => 42 ;; macro expand: ;; (let ((first-result (* 1 -10))) ;; (if (test? first-result) first-result 42)) The 1st question is: Can I use first-result in the exp2 position? For example, (gor (* 1 -10) (+ first-result 42)) ;; the macro expand is ;; (let ((first-result (* 1 -10))) ;; (if (test? first-result) first-result (+ first-result 42))) I hope it return 32, but Racket complains ERROR: first-result unbounded identifier. So if I want to use the first-result in the exp2, what should I do?

The 2nd question is: Is the following approach safe in Racket? (define-syntax gor2 (syntax-rules () [(gor (first-result) exp1 exp2) (let ((first-result exp1)) (if (test? first-result) first-result exp2))])) Now I can use first-result in the exp2, (gor2 (fr) (* 1 -10) (+ fr 42)) but I’m not sure whether this approach is safe in general.


soegaard2
2022-3-2 13:31:09

First of all: The last approach is safe.


soegaard2
2022-3-2 13:32:40

In (define-syntax gor (syntax-rules () [(gor exp1 exp2) (let ((first-result exp1)) (if (test? first-result) first-result exp2))])) The variable first-result is introduced by the macro - and its scope is therefore limited to the body of the let.


soegaard2
2022-3-2 13:34:20

Each invokation of gor introduces a new variable first-result . So as an approximation, you can think each invocation introduing first-result1, first-result2 etc.


soegaard2
2022-3-2 13:34:34

That means (gor (* 1 -10) (+ first-result 42)) expands to:


chansey97
2022-3-2 13:35:09

Thanks. Can the pattern of the 2nd approach be used generally as a convention? For this kind of use case.


soegaard2
2022-3-2 13:35:26

(let ((first-result7 (* 1 -10))) (if (test? first-result7) first-result7 (+ first-result 42)))]))


soegaard2
2022-3-2 13:35:45

So the first-result variable from exp2 is unbound.


soegaard2
2022-3-2 13:36:33

> Can the pattern of the 2nd approach be used generally as a convention? For this kind of use case. Yes, when syntax-rules is used and you need to bind an “outside” variable it needs to be part of the macro call.


chansey97
2022-3-2 13:48:50

Nice! I think this feature is really amazing. (define-syntax gor2 (syntax-rules () [(gor (first-result) exp1 exp2) (let ((first-result exp1)) ...very-complex-body...)])) (gor2 (fr) (* 1 -10) (+ fr 42)) Can I think that the fr in the 3rd argument of the macro call can always refer to the first argument fr, no matter how complex the let body is? (Of course (+fr 42) itself is not a binding form in the macro call)


laurent.orseau
2022-3-2 13:51:29

I often want #:when inside the body of a for/list , rather than #:break , which would avoid an outside (filter values ...) or reverting to for/fold . Since I’m probably not the only one, does that mean there are technical difficulties to make this work?


soegaard2
2022-3-2 13:53:16

@chansey97 Depends on how the question is interpreted. If you do this: (gor2 (fr) (* 1 -10) (let ([fr 1]) (+ fr 42))) then the result is 43.


soegaard2
2022-3-2 13:56:56

@laurent.orseau Do you want to allow this?

(for/list ([x 5]) x #:when (< x 2) (+ x 1))


soegaard2
2022-3-2 13:57:17

And what do you expect the result to be?


laurent.orseau
2022-3-2 13:57:28

The other option is #:do but for some reason I keep forgetting about it, and it feels in the wrong place—but it’s still more general since it can interleave clauses, true.


chansey97
2022-3-2 13:57:30

I see, thanks.


laurent.orseau
2022-3-2 13:58:26

Looks like a coding interview :smile: I expect ’(1 2)


laurent.orseau
2022-3-2 13:58:39

@soegaard2


laurent.orseau
2022-3-2 14:10:48

This still feels clunky though: (for/list ([(key counts) (in-hash pdb2)] #:when (for/or ([x counts]) (> x 0)) #:do [(define θs (hash-ref pdb key)) (define ∑θ (for/sum ([x θs]) x)) (define gs (for/vector ([θ θs] [c counts]) (if (zero? θ) θ (* c (log (/ ∑θ 1. θ))))))] #:when (for/or ([g gs]) (> g 0))) (cons key gs)) and I’d rather write (for/list ([(key counts) (in-hash pdb2)]) #:when (for/or ([x counts]) (> x 0)) (define θs (hash-ref pdb key)) (define ∑θ (for/sum ([x θs]) x)) (define gs (for/vector ([θ θs] [c counts]) (if (zero? θ) θ (* c (log (/ ∑θ 1. θ)))))) #:when (for/or ([g gs]) (> g 0))) (cons key gs))


soegaard2
2022-3-2 14:12:53

Is there a rule that can generate the first version from the second?


soegaard2
2022-3-2 14:13:49

If there is no #:when in the body - job done.


laurent.orseau
2022-3-2 14:15:35

I guess, for the rest, just put the #:when clauses as is, and put the rest in #:do [...] clauses, before the last paren of the clauses.


soegaard2
2022-3-2 14:15:40

A single #when : (for-something (clauses ...) expr ... #:when test more-expr ...) Is that simply: (for-something (clauses ... #:do [expr ...] #:when test) more-expr ...)


laurent.orseau
2022-3-2 14:17:19

I think so


soegaard2
2022-3-2 14:18:57

If so, then we could make an improve-for macro and write: (improve-for improved-for/list for/list) to generate improved-for/list .


soegaard2
2022-3-2 14:20:36

It would only work for for constructs with the same syntax as for, for/list etc. though.


laurent.orseau
2022-3-2 14:29:55

Indeed


soegaard2
2022-3-2 14:39:27

There is a helper that splits the body apart in #:break and #:final in for.rkt, so maybe there is a way to shoehorn in #:when too.



massung
2022-3-2 14:48:45

Keywords is outside where it belongs. It’s either an error (not sure what racket does compared to CL here) or the answer is ’(1 2 3 4 5) because - like x - the keyword value is just ignored.


massung
2022-3-2 14:52:19

Doh. Just saw the previous posts. Lol. Ignore me.

I’ll agree that there are many times I want the conditional outside the iteration block.

Although, usually when like that I more wish that racket had something like for/flatten, and then I could just return null to have the value ignored.


massung
2022-3-2 15:12:09

What’s today’s special date for the PLT logo? Birthday of PLT?


sorawee
2022-3-2 15:30:32

Texas Independence Day


sorawee
2022-3-2 15:31:12

The PLT group was formed at Rice University, in Texas.


massung
2022-3-2 15:36:37

Certainly having a version (or just making all versions) of for that ignore #<void> values would be nice.


tomdtron
2022-3-2 16:12:53

@tomdtron has joined the channel