
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.

First of all: The last approach is safe.

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.

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.

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

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

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

So the first-result
variable from exp2
is unbound.

> 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.

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)

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?

@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.

@laurent.orseau Do you want to allow this?
(for/list ([x 5])
x
#:when (< x 2)
(+ x 1))

And what do you expect the result to be?

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.

I see, thanks.

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

@soegaard2

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))

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

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

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.

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

I think so

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

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

Indeed

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.


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.

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.

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

Texas Independence Day

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

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

@tomdtron has joined the channel