I'm trying to, more or less, recreate a let
construct via syntax-rules
but it seems to be tripping on the use of two ellipses.
I tried writting it out as so:
(define-syntax if-let
(syntax-rules ()
[(_ (([binding value] ...)
([binding2 funct value2])
([binding3 value3] ...)) then else) (let ([binding value] ...
[binding2 value2]
[binding3 value3] ...)
(if (funct binding2) then else))]))
I figured the distinct pattern in the middle of [binding2 funct value2]
would be able to provide a clear division, in terms of pattern, of when the first pattern ended and when the second one would begin but I keep getting back the error in the title.
The intended result is to be able to do something like
(if-let ([var1 1]
[diff null? '(1 2 3 4 5)]
[var2 2])
var1
var2)
and get back 2
but being able to have as many var
s before and after diff
as remotely desired so the order of the variables used, ultimately, doesn't matter.
Am I missing something obvious? And is this pattern feasibly able to be done with hygenic macros? Thanks for any help!
This is possible with a helper macro, to do the recursion necessary for finding the funct
things in the middle.
(if-let-helper processed-bindings processed-conditions input-binding-conditions then else)
As it recurs, it transfers the information in input-binding-conditions
into the
processed-bindings
and processed-conditions
, stopping when the
input-binding-conditions
is empty.
(define-syntax if-let-helper
(syntax-rules ()
[(_ ([bnd val] ...) (cnd ...) () then else)
(let ([bnd val] ...)
(if (and cnd ...) then else))]
[(_ ([bnd val] ...) (cnd ...) ([binding value] . rest) then else)
(if-let-helper ([bnd val] ... [binding value]) (cnd ...) rest then else)]
[(_ ([bnd val] ...) (cnd ...) ([binding funct value] . rest) then else)
(if-let-helper ([bnd val] ... [binding value]) (cnd ... (funct binding))
rest then else)]))
(define-syntax if-let
(syntax-rules ()
[(_ (binding-funct-value ...) then else)
(if-let-helper () () (binding-funct-value ...) then else)]))
Using it:
> (if-let ([var1 1]
[diff null? '(1 2 3 4 5)]
[var2 2])
var1
var2)
2
To explain this, I will step through how this example processes each clause. It initially turns into this if-let-helper
call:
(if-let-helper
()
()
([var1 1]
[diff null? '(1 2 3 4 5)]
[var2 2])
var1
var2)
The first two lists start empty because it hasn't processed anything yet.
(if-let-helper
([var1 1])
()
([diff null? '(1 2 3 4 5)]
[var2 2])
var1
var2)
At this point it has processed the first clause and added a binding-value pair to the first "processed-bindings" list. However, there was no funct
in the first clause, so it didn't add a condition to the second "processed-conditions" list.
(if-let-helper
([var1 1]
[diff '(1 2 3 4 5)])
((null? diff))
([var2 2])
var1
var2)
At this point it has processed the first two clauses, and added a (null? diff)
condition to the second list because it saw the funct
there in the second clause.
(if-let-helper
([var1 1]
[diff '(1 2 3 4 5)]
[var2 2])
((null? diff))
()
var1
var2)
It this point it has processed all three clauses, so it hits the base case and transforms into the final let
and if
:
(let ([var1 1]
[diff '(1 2 3 4 5)]
[var2 2])
(if (and (null? diff))
var1
var2))