Hygienic macro r7rs : Return second expression value

134 views Asked by At

I'm currently learning some r7rs and I am trying to implement a macro 'begin' as following :

(begin0 expr0 expr1 ... expr2)

With expr being a regular expression (Like (set! x (+ x 1)))

And begin0 as a macro that evaluates all the expression but return only the expr1 result.

For example :

(let ((year 2017))
(begin1 (set! year (+ year 1))
  year
  (set! year (+ year 1))
  year)) 

It musts return 2018

I've created a begin function first :

(define-syntax begin0
 (syntax-rules ()
  ((begin-0 body-expr-0 body-expr-1 ...)
   (let ((tmp body-expr-0)) body-expr-1 ... tmp))))

And now, I'm trying to understand how I can do to return the value of "body-expr-1" ? I've done the following code, but it says that I'm missing some ellipsis and I don't understand how to do it.

(define-syntax begin1
  (syntax-rules ()
    ((begin1 body-expr-0 body-expr-1 ... body-expr-2)
     (let ((tmp body-expr-0) body-expr-1 ... tmp)
       (cond (eq? tmp body-expr-1)
              (begin . tmp))))))

I hope that it is understandable enough, thanks for the answers.

2

There are 2 answers

1
Sylwester On BEST ANSWER

This can be done, but the macro will interfere such that you cannot do all the things with begin1 as with begin.

(define-syntax begin1
   (syntax-rules ()
     ((_ expr0 expr1 exprn ...)
      (begin
        expr0
        (let ((result expr1))
          exprn ...
          result)))))

The code that does not work is this:

(begin1
  (define global1 10)
  test3
  (define global2 20))

The reason is obvious. It expands to:

(begin1
  (define global1 10)
  (let ((result~1 test3))
    (define global2 20)
    result~1))

The second define will be changed to a letrec such that the variable global2 is only available for the duration of the let. I have no fix for this since it requires you to be able to do global define from a closure.

begin1 is rather strange feature. In Racket and perhaps other Scheme dialects we have begin0 that returns the result of the first expression. This is very useful. eg. here is a counter:

(define (get-counter from)
  (lambda ()
    (let ((tmp from))
      (set! from (+ from 1))
      tmp)))

And with begin0:

(define (get-counter from)
  (lambda ()
    (begin0 
      from
      (set! from (+ from 1)))))

In Racket begin0 is a primitive. So it is a form supported in the fully expanded program and thus implemented in C, just like begin..

1
Lucas M On

So, I found a possible way to do it, I didn't though we could just ask a condition on the immediate value :

(define-syntax begin1
  (syntax-rules ()
    ((begin1 body-expr-0 body-expr-1 body-expr-2 ...)
       (if body-expr-1
          (write body-expr-1)))))