3

I'm trying to write a macro in Scheme for Picolisp style let expressions, let's call this macro let-slim. In order to be more terse (like Picolisp) I want their usage to look something like this when declaring only one variable

(let-slim var-name initial-value
  (display var-name))

Or something like this to declare any number of variables (note that this is pseudocode, I wouldn't actually include the elipses)

(let-slim (var-name-1 initital-value-1
           var-name-2 initital-value-2
           ...
           var-name-n initital-value-n)
  (+ var-name-1 var-name-2 ... var-name-n))

The first usecase is fairly trivial to write a syntax-rules matching pattern for, but the latter I am struggling with.

This doesn't work because only init gets repeated

(define-syntax let-slim
  (syntax-rules ()
    [(_ (var init ...) body ...)
     (let ((var init) ...)
       body ... )]))

This doesn't work because it's considered a misplaced elipsis

(define-syntax let-slim
  (syntax-rules ()
    [(_ (var ... init ...) body ...)
     (let ((var init) ...)
       body ... )]))

And this doesn't work because I need to use parens at the reference point (which means it changes absolutely nothing as compared to the built-in let)

(define-syntax let-slim
  (syntax-rules ()
    [(_ (var init) ...) body ...)
     (let ((var init) ...)
       body ... )]))

So, is there a way to repeat 2 variables in syntax-rules without needing to wrap them in parens, or do I need to use a different macro system (ie syntax-case or defmacro)?

2 Answers 2

6

It's not optimal doing this with syntax-rules, but since it is turing complete it can be done:

(define-syntax let-slim
  (syntax-rules (pair)
    ((_ pair bindings () body)
     (let bindings . body))
    ((_ pair (acc ...) (k v . rest) body)
     (let-slim pair (acc ... (k v)) rest body))
    ((_ (elements ...) . body)
     (let-slim pair () (elements ...) body))))
Sign up to request clarification or add additional context in comments.

Comments

3

It's not possible to do this in one go with syntax-rules ... feature, but you may be able to do it with syntax-rules using recursion:

(define-syntax let-slim
  (syntax-rules ()
    ((let-slim (var-1 val-1 . rest) . body)
     (let-slim var-1 val-1 (let-slim rest . body)))
    ((let-slim var val . body) 
     ;; single binding case you already implemented
     ))

The only problem is that syntax-rules can't tell that 'var' is supposed to be a symbol. You wont get good error messages from a macro like this (for example if it's used with an odd number of var/val bindings). It may be better to implement this macro with syntax-case. The reason it's difficult to implement is because it's kind of violating the idea of using a pair of brackets for each AST node.

1 Comment

I do not believe your example code works as-is. In the first case, the body should be inside of the recursive call to let-slim, and written in that manner it seems to eventually expand to a reference to (let-slim () . body), which has no appropriate matching case. I got it to work by adding such a case, but that would allow the end-user to use (let-slim () . body) at any point, which might be a little silly. I think I'll look into using syntax-case, but if you know of a solution to that last issue let me know.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.