简体   繁体   中英

Hygienic macro r7rs : Return second expression value

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.

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM