guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

macro helpers


From: Stefan Israelsson Tampe
Subject: macro helpers
Date: Tue, 12 Sep 2017 20:09:43 +0200

Writing efficient macros is a bit difficult. Let me explain by using an example. The background
is that I maintaining a python compiler and python like object system and would like to program
a scheme macro that would be the scheme counterpart to various python construct. For fun
consider pythons for loop. it's looping depending on iterators and have break and continue.

Here is a hypothetical for loop:

(for lp ((x : I)) ((c 1))
     (lp #:continue (+ x c))

     #:final
     c)

The python iterators signals the end of the loop with raising en exception which is not too costly and we will return the #:final as a value at that point. This is a mix of scheme and python. Now what we
can do further is to introduce break,continue and break and final as

(lp #:continue c)   e.g. continue with c
(lp #:break      c)   e.g. break with c
(lp #:final)             e.g. execute final with current c

This has a great potential of a easy generalization of python for loops an implementation could be like, its very slow though, here is a take on the implementation which describes the macro 
(more work is needed, not a propper pattern here)

(define-syntax for
  (lambda (x)
    (syntax-case x ()
      ((for lp ((x ... : E) ... (c n) ...) code ... #:final fin ...)
       (with-syntax (((It ...)       (generate-temporaries #'(O ...)))
                     ((cc ...)       (generate-temporaries #'(c ...)))
                     (((x1 ...) ...) (generate-temporaries #'((x ...) ...)))
                     (((x2 ...) ...) (generate-temporaries #'((x ...) ...))))
         #'(let ((It E) ... (c n) ... (x 'None) ... ...)
             (let/ec lp-break
               (catch IteratorException
                 (lambda ()
                   (letrec ((enclosing
                             (lambda (cc ...)
                               (set! c cc) ...
                               (call-with-values
                                   (lambda () (next It))
                                 (lambda (x2 ...)
                                   (set! x1 x2) ...))
                               ...
                               (set! x x1)
                               ... ...
                               (call-with-values
                                   (lambda ()
                                     (let/ec lp-continue
                                       (define (lp tag . args)
                                         (cond
                                          ((eq? tag #:continue)
                                           (apply lp-continue args))
                                          ((eq? tag #:break)
                                           (apply lp-break args))
                                           ((eq? tag #:final)
                                             (lp-continuation #:final))))
                                          code ...))))
                                 (lambda args
                                   (if (eq? (car args) #:final)
                                       (throw IteratorException)
                                       (apply enclosing (cdr args))))))))
                     (enclosing c ...)))
                 (lambda q fin ...)))))))))
   
The value of tail position is transfered to the next iteration of the loop. It's not functional. But this is for the full featured version in which lp may be transferred to another function and there inside a loop called e.g.all crazy things, the easy steps would be to have full control in multiple loops.
Note how this enables great refactoring of functions with many loops inside loops. Anyway for normal loops this is really really slow, and one would really like to have streamlined code when (lp #:continue ...) is used at tail positions and for cases where we can prove that lp is never used in any advanced configuration, we would like to know if lp, (lp #:continue ..) (in tail position) and finally if (lp #:break ...) is used or not. But how to do this at the macro level? I don't think that we
have any good history of optimizing this case!

WDYT




reply via email to

[Prev in Thread] Current Thread [Next in Thread]