This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

Better then loop/iterate?

42 posts · 31425 views

Update: I made a little project on Berlios I don't expect to work on much, though. The dayly tarball hasn't come yet,(as of 20-3-'09) btw.

As i have said a couple of times, i have been messing with making a loop-like micro. Loop itself is not that good; not being lispy. Iterate improves, but 'for .. in ..' sort of notation seems silly to me. (I'd have done it for-in .. ..)

So i messed around making a loop thing for myself. I noted that one important thing iteration does is create variables for the user that are implicit, and which are then used either to collect something or to recurse over something. Symbol-macrolets and macrolet are useful in this too. Also, sometimes you want the 'incrementation' to be after the body, implicitly.
And it should also be extensible, of course. (And usually this means any of the keywords in it should be defined via the normal extending way.)

At first i made a lot of stuff i threw away later, firstly because the loop had its own language separate from common-lisp, and it irked me to have to redo even the simple stuff like: when, cond, unless, secondly because parsing the common lisp language for stuff that implied variables was a simply bad idea. Finally i landed on what i will be posting here.

Basically now what you enter the looping macro consists of two parts, the one from which the information in a let, symbol-macrolet, flet, macrolet, and the things that have to go after the main body are gathered, and the main body, which can use all those. So here is the code: (less then 100 lines of code dunno how to let mac count 'em. Anyway, might want to copy it to an editor for better readability.)
(defpackage #:umac
  (:use #:common-lisp)
  (:export umac def-umac
	   values-default values-d
	   collecting appending summing until while force-return))

(in-package #:umac)

(defvar *have-umac-hash* (make-hash-table))

(defun first-match (list eql-to &optional (match-fun #'eql))
  (dolist (el list)
    (when (funcall match-fun el eql-to)
      (return el))))

(defun append-nonmatching (list appended &optional (match-fun #'eql))
  "Append elements of appended if match-fun returns false."
  (let (left)
    (dolist (a appended)
      (unless (first-match list a match-fun)
	(setf left `(,@left ,a))))
    (append list left)))

(defun delist (x) (if (listp x) (car x) x))

(defmacro setf- (change set &rest args)
  `(setf ,set (,change ,set ,@args)))

(defmacro umac ((&rest rest) &body body)
  "Umac allows you to make variables and functions/macros manipulating them
in one sentence.
Elements of rest are either references to extensions, or assoc-lists, when the \
latter, :let ->into let, :flet into flet, :mlet -> into macrolet, \
:smlet -> into symbol-macrolet, :post -> added behind the body."
  (let (got-let got-smlet got-flet got-mlet got-post)
    (do ((iter rest iter)) ((null iter) nil)
      (symbol-macrolet ((el (car iter)))
	(if (symbolp (car el)) ;If symbol get what the *have-umac-hash* provides.
	    ;Uses iterator as a 'stack' too.
	  (setf iter `(,(funcall (gethash (car el) *have-umac-hash*) el)
		       ,@(cdr iter)))
	  (flet ((append-nm (list append-key) ;Otherwise Just process it.
		   (append-nonmatching list (cdr (assoc append-key el))
				       (lambda (a b) (eql (delist a) (delist b))))))
	    (setf- append-nm got-let   :let)
	    (setf- append-nm got-smlet :smlet)
	    (setf- append-nm got-flet  :flet)
	    (setf- append-nm got-mlet  :mlet)
	    (setf- append-nm got-post  :post)
	    (setf- cdr iter)))))
    `(let (,@got-let)
     (symbol-macrolet (,@got-smlet)
     (flet ((values-default ()
	      ,(flet ((get-var (name)
			(when (first-match got-let name
				(lambda (el eql-to) (eql (delist el) eql-to)))
			  name)))
		 `(values ,(get-var 'ret)   ,(get-var 'val-0) ,(get-var 'val-1)
			  ,(get-var 'val-2) ,(get-var 'val-3) ,(get-var 'val-4)
			  ,(get-var 'val-5) ,(get-var 'val-6) ,(get-var 'val-7))))
            ,@got-flet)
     (macrolet ((values-d () (values-default))
		,@got-mlet)
       (do () (nil nil)
	 ,@body
	 ,@got-post)
       (values-default)))))))

(defmacro def-umac (name (&rest arguments) &body body)
  "Defines a umac for you. Return either an assoc-list, or a reference to\
 another extension, with arguments."
  (let ((args (gensym)) (self (gensym)) (gname (gensym))
	(docstr (when (stringp (car body)) (list (car body)))))
  `(let ((,gname ,name))
     (setf (gethash ,gname *have-umac-hash*)
	   (lambda (,args)
	     ,@docstr
	     (destructuring-bind (,self ,@arguments) ,args
	       (unless (eql ,self ,gname)
		 (error "First argument not repeat of have-umac"))
	       ,@(if docstr (cdr body) body)))))))
Here is how it works: The thing that determines what is in the *let's is a list of association lists with different names referring them. :post refers to stuff that has to go after the body.
If the first element is a symbol rather then an association list, that means that an extension is used, and extension is just a function with some arguments, that produces the association lists as described before. Extensions may also refer to other extensions.(I just noticed, that it may only be one other, but i guess an extension could probably fix it.)
Finally, the whole thing returns the following variables, nil if they do not exist: (values ret val-0 val-1 val-2 ...

Here are some basic extensions.(They don't have to be bound to keywords, of course!)
(in-package #:umac)

(def-umac :list (&optional (list-into 'ret) initial)
  "Listing stuff; collecting, appending"
  `((:let (,list-into ,initial))
    (:flet (collecting (&rest collected)
	     (setf- append ,list-into collected))
           (appending (&rest appended)
	     (dolist (el appended)
	       (setf- append ,list-into el))))))

(def-umac :sum (&optional (sum-onto 'ret) (initial 0))
  "Summing onto a variable; summing"
  `((:let  (,sum-onto ,initial))
    (:mlet (summing (&rest added)
	     `(setf- + ,',sum-onto ,@added)))))

(def-umac :ops (&optional (onto 'ret) initial)
  "Changing stuff with any operation."
  `((:let (,onto ,initial))
    (:mlet (op (op-name &rest args)
 		 `(setf- ,op-name ,,onto ,@args)))))

(def-umac :return ()
  "Returning; until, while. WARNING uses (return), will behave such!"
  `((:mlet (force-return (returned)
	      `(setf ret ,returned))
           (until (&rest and)
	     `(when (and ,@and) (return)))
	   (while (&rest and)
	     `(unless (and ,@and) (return))))))

(def-umac :single-round ()
  "Return after single run of umac. (Put at end!)"
  `((:post (return))))

(def-umac :for-list (var list &optional (end-cond :stop) (iter (gensym)))
  "An iterator over a list. Set end-cond to :continue to not stop when \
list runs out."
  `((:let   (,iter ,list)) (:smlet (,var (car ,iter)))
    (:post  (setf- cdr ,iter)
            ,@(case end-cond
	       (:continue nil)
	       (:stop `((when (null ,iter) (return))))))))
And some usage, of course: collecting numbers.(Hmm should've made a :repeat.. annoying to develop on other computer then you post with)
(umac ((:return) (:list) (:sum i))
  (until (> i 10))
  (summing 1)
  (collecting val-0))
New list, adding ten to old list.
(umac ((:list) (:for-list el (list 1 2 3 4 5 6 7 8)))
  (collecting (+ el 10)))

Last edited by Jasper on , edited 3 times in total.

Re: Better then loop/iterate?

When I have to do something "loopy", I look for a fitting language construct in Common Lisp in roughly the following order:
  • DOTIMES, DORANGE (a simple macro to write)
  • Built-in list or sequence manipulation: MAPCAR, MAPLIST, MAPCAN, MAPCON, MAPC, MAPL; MAP, REDUCE, COUNT (-IF, -IF-NOT), FIND, POSITION, SEARCH, REMOVE (-IF, -IF-NOT, -DUPLICATES), DELETE (-IF, -IF-NOT, -DUPLICATES), (N)SUBSTITUTE (-IF, -IF-NOT), MAP-INTO
  • DOLIST
  • Recursion (with tail call optimization)
  • DO
  • DO*
Of course, it is always nice to have another tool in the box. About where in this list would you put your construct?
"Just throw more hardware at it" is the root of all evil.
Svante

Re: Better then loop/iterate?

It might be crappy but this is currently pretty much the sequence. My attitude is a little like 'just do it damit'
  • dolist, dotimes.
  • loop (which is pretty bad..)
  • do (ok, but not too readable.)
  • If i need a stack, recursively. (Unless a stack happens to fall in my lap, like the code in OP)
The ones based on functions seemed a little weirdly named. Also, when you collect stuff in some way, like summing, consing, appending, working with a stream, etcetera, it would seem better to let the callback do the work, because otherwise the function with function as argument has to gather all the stuff internally, and every different way would need it's own implementation. Unless you provide functions to specify how it, but that would make it a bit more complicated to use.

If you use callbacks to gather, you only need to have different functions for different ways of iterating. This is btw another advantage of this way over loop and iter, you can do: (Should've called :single-round \:once..) (all untested)
(defun add-1 (list)
  (umac ((:list) (:single-round)) (map nil (lambda (x) (collecting (+ x 1))))))
Hmm, i don't think that is best, maybe instead of the multitude of map* functions, use reduce?
(defun consa (list add) '(,@list ,add))
(defun add-1 (list)
  (reduce (lambda (out el) (consa out (+ el 1)) list))
Here i find myself wishing i could make the lambda with a stack language: '1 + consa' would produce that. I guess that wouldn't be readable enough either though. Or do the lambda cheaper ($ (consa $1 (+ $2 1)), but also a little bit dense..

I have thought about higher functions, like when i thought about having (not function) be equivalent to (lambda (&rest rest) (not (function @rest))), or if there is spillover: (f-a f-b) eqv to (lambda ([stuff of f-b] [stuff of f-a]) (f-a (f-b [stuff of f-b]) [stuff-of-f-a])). Maybe i should make a little macro, maybe instead (ho () f-a f-b) can do that, or even (ho (f-d) f-a f-b f-c), that would make reduce look a lot more attractive.(f-d being arguments before f-a f-b f-c, etc. Ah shit i don't really see how to do the lambda in the defun right now. I need to think about this more.
With this stuff using functions taking functions as arguments would be much more attractive, not having to have (lambda (arguments) ..) floating around everywhere.

The umac macro i made here makes me doubt. Lets ask the question how we would make a lisp that does this with regular macros. I'd do it with scope; defvars, defun, defmacro, defsymbol-macro limited to the bodies of progn, lambda, etc. You could do pretty the thing i did with umac by having a scope-transparent-progn, and making the macro output a scope-transparent-progn have the defvars etc. in it. But then i ask of myself why still have a let, flet, macrolet?

Somewhat relatedly, i am also doubting whether s-expressions are really the way we should write everything. Sure, what we write should trivially be converted to s-expressions, but there is a lot that does that. not~expr -> (not expr) for single-argumented functions and numerically, a + b -> (+ a b), same for *, etc. would need to work out precidence.
The thing here that is related that we could write (def symbol expr) for a variable and (def (fun-name arguments) (progn body)) for functions, now we can think about scoping and call {...} (progn ...) and a := b (def a b), and we would get functions written more like ocaml or something.
(defmacro for-list (el list &optional (iter (gensym))
  (post-body `(progn ,iter = (cdr ,iter) /*Add to post-body*/
                                (when (null iter) (finish)))) /*Stop at end of list.*/
  `(transparent-progn
      ,iter := ,list
      (def-symbol-macro ,el (car ,iter))))

(defmacro equip-listing (&optional (out-var 'ret)) /*Ret being default return.*/
  `(transparent-progn
      (collecting &rest args) := { (append ,out-var (list args)) }
      (appending &rest args) := { (append ,out-var args) }))

(add-list list &optional (add 1)) := 
  { (for-list el list) (equip-listing)   /*Make this scope one that iterates a list, and get stuff to collect with.*/
    (collecting (+ el add))
  }
That might not look very lispy, but the layer of veneer on the s-expressions is very thin. (Note that definining functions with := only has an expression, not a body; you need to either use {} to get a body.) It kills a tonne of hooks, even more could be removed with this idea but i decided not to overload it with too many ideas.

Re: Better then loop/iterate?

(defun add-1 (list)
  (mapcar (lambda (x) (+ x 1)) list))
"Just throw more hardware at it" is the root of all evil.
Svante

Re: Better then loop/iterate?

I hate loop and love iterate. One problem (which is mostly not a big deal) is that iterate sometimes does not respect order of the computation (I do not have examples of this right now), but it works quite well. The only big problem with iterate is that the symbols you have to use are the ones exported from iterate package. You cannot, for instance,
(iter:iter (:for i from 0 to 10) (print i))
You can't even
(iter:iter (for i from 0 to 10) (print i))
unless the package :iter is being used (i.e. with (use-package :iter)). I would prefer to be able to use keywords instead.

The thing I most like about iterate is that it macroexpands into very readable code (except that it macroexpands every macro being used in the body of the loop) - SBCL's loop macroexpansion, for instance, is a nightmare. Iter also has the advantage of not doing function calls whenever they are not needed (of flets) - it creates just lexical bindings.

By the way, one performance hint. Collecting elements into a list does not need to walk into the list until the last position. You can explicitly create a collector this way:
(let ((list nil)
      (last nil))
  (flet ((collect (elt)
           (if list
               (setf list (list elt) last list)
               (setf (cdr last) (list elt)))))
    (dotimes (i 100)
      (collect i)))
  list)

Re: Better then loop/iterate?

@Harleqin: I get it, keep it simple, stupid :) I went a little crazy with alternative syntax. Might be a good way to attract people that are crazy with syntax, or hate parentheses to lisp though..

I agree just by comparing loop with the docs of iterate that the latter is superior. It also looks like i underestimated iterate, according to that, iterate actually can read normal macros, actually expanding them to look inside. I had thought of that, but wanted to take an easy route after all that mucking about.

Unless there are some other snags, iterate is probably much better then what i made. I think i will just finish what i have. (I already removed that silly assoc list.) Even the problem of having to use the package doesn't seem very important. They're probably not keywords because some of them are regular macros, and they didn't want to be inconsistent.

Thanks for the hint, I don't get it though. List starts nil, whenever collect is called, the second clause is called, only affecting last, so list stays nil.. Also what i already got seems straightforward enough.

Re: Better then loop/iterate?

Hum, I had fixed that, but copied the wrong version. Basically, it works with 2 variables, the "list" itself and "last", which is analogous to (last list) (i.e. if you call (last list) you obtain the same value which last is bound to)..
(let ((list nil)
      (last nil))
  (flet ((collect (elt)
           (if (null list)
               (setf list (list elt) last list)
               (setf (cdr last) (list elt) last (cdr last)))))
    (dotimes (i 100)
      (collect i)))
  list)

Re: Better then loop/iterate?

Implemented. I tried to get it to work for a list directly, but somehow (setf last (last last)) doesn't get it to work properly. I just did it by adding them one by one. I have (collecting &rest collected), instead of a single argument; i wouldn't know what to do with the rest of the arguments anyway. The specification that we are listing already also specifies a variable to list into. Hmm, maybe next to a :list i need a :list-into extension..

Spotted a disadvantage with the optimization, other extensions which do not play well with the 'last variable, break it. And currently they can't work well with it; 'last is a gensym. Added a function fix-list that sets last correctly.
gugamilare wrote:The thing I most like about iterate is that it macroexpands into very readable code
I can't make umac do this, not if i don't look into the body to see if the flets/macrolets etc. are actually used. At least excess unused variables/flets shouldn't make.

I think i will put it online somewhere under the public domain, as a small 'you see what you do with this, please try iterate first' thing.

Re: Better then loop/iterate?

Jasper wrote:Implemented. I tried to get it to work for a list directly, but somehow (setf last (last last)) doesn't get it to work properly. I just did it by adding them one by one. I have (collecting &rest collected), instead of a single argument; i wouldn't know what to do with the rest of the arguments anyway.
(let ((list nil)
      (last nil))
  (flet ((collect (&rest elts)
           (if (null list)
               (setf list elts last list)
               (setf (cdr last) elts last (last elts)))))
    (dotimes (i 100)
      (collect i)))
  list)
This should do for multiple arguments.

Re: Better then loop/iterate?

Call me old-fashioned... I just use LOOP. :shock: Like everybody else, I reach for DOLIST and DOTIMES for the simple stuff, as well as MAPCAR and friends for that type of work, but then it's pretty much straight to LOOP for everything else.

That said, I'm interested in ITERATE. That seems close to LOOP but helpful for curing some of the general LOOP annoyances, as well as being extensible.
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Re: Better then loop/iterate?

@gugamilare: Imbarrassed i didn't find that myself, but at least you forgot to check (collect i i)

I guess loop is alright, but it is so damn ugly. It is a stranger in s-expression land. That, in itself is fine by me, but it is also unclear how to convert the thing into s-expressions.

The only thing that minorly annoys about iterate at this point that it uses stuff like (for k from 0 to 100), not many macros do that afaik. However this is easily fixed:
(require :iterate)
(in-package #:iterate)

(defmacro for-range (var from to)
  `(for ,var from ,from to ,to))

(iter (for-range k 0 10)
      (collect k))
...Excellent

Re: Better then loop/iterate?

Jasper wrote:I guess loop is alright, but it is so damn ugly. It is a stranger in s-expression land. That, in itself is fine by me, but it is also unclear how to convert the thing into s-expressions.
Well, LOOP is just one big sexpr. That is, people have always said that LOOP is annoying because it doesn't use more parentheses, but it's never been a big problem for me. I'm not sure that more parentheses would buy me much. Sure, there are definitely advantages of sexpr movement in the editor, etc., but everything in a DO subexpression is a sexpr anyway, and that's where I spend most of my time. I use LOOP in macros all the time and don't really have problems because of lack of sexprs; everything expands as it should. You might have to use PROGN forms a bit to make sure things group, but that's all doable. In other words, the complaint seems to be more of an aesthetic, but less of a practical matter. I have no doubt that somebody could show a situation where LOOP didn't do well in another large macro, but in practice, for me at least, that seems more of a theoretical argument.

IMO, the biggest pains with LOOP are remembering how multiple termination clauses interact with other clauses and such. In other words, it's that it's a whole big complex macro, whatever it's syntax. And I'm assuming ITERATE has some of the same issues since it's basically LOOP with more sexprs. That said, I'm not an ITERATE guru at this point; I have just skimmed the top-level documentation and never used it in practice.
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Re: Better then loop/iterate?

findinglisp wrote:Well, LOOP is just one big sexpr.
Well, without messing with the reader, i would have to be at least one, no?

I agree that much of my reaction to loop is esthetic/emotional, though. And i am also a novice in iterate. I know some real advantages though:
  • Extensible via regular macros and drivers. Would've been nice if they allowed 'regular macros' but only inside the iterate construct, since many of the macros wouldn't make sense outside anyway.
  • The body of iter is a regular body. You can just write any code there if it doesn't collide with iters stuff.
  • An advantage of looking through macroexpansion output is that you can put collector anywhere, including callbacks: iter can:
    (in-package #:iterate)
    (defun repeated (n fn) (dotimes (k n) (funcall fn n)))
    (iter (repeat 1)
          (repeated 5 (lambda (x) (collect x))))
    And loop cant:
    (loop repeat 1
      do (repeated 5 (lambda (x) (collect x))))
    So iterate can be used in callbacks. Very useful, imo. (I'd even make a (iter-once ..) for it.
Hmm, weird that the manual doesn't list some of these.

umac has all these advantages, plus a bunch of disadvantages(also relative to loop) are:
  • umac macroexpansion produces a whole bunch of stuff in let, flet, macrolet etc. that are not needed. It can't scan whether they are needed.
  • Special stuff that creates toplevel stuff in flet, macrlet, etc. have to be in the list on top of the code. (Although one might not care, not going to read macroexpansion anyway.)
  • It misses some features, many of these can still be fixed, but some not through extensions. Don't know how to do this one though. (Maybe prog and go)
On the other hand, umac is a bit simpler, may be simpler to understand in some cases, and not scanning all the macroexpansions might help.

What is weird that loop is in the specification of common lisp. Didn't these guys hear of 'standard libraries'? They should've called it that and packaged it along. There is some evidence that the powers that be are not(/do not act as if they are) always the cleverest. Exhibit C, A is the credit crisis, and B is the use of many of those other computer languages.

Re: Better then loop/iterate?

I agree that loop shouldn't be standard. It is a monster macro which was firstly created to make the learning process easier for beginners.

What most annoys me is that you can't write
(loop for elt in list
      (if elt (collect elt)))
I am obligated to write
(loop for elt in list
      if elt collect elt)
This makes it impossible to do a simple, intuitive thing like this:
(loop for elt in list
      (case elt
        ((:foo :bar) (do-something-with elt)
        (nil nil)
        (t (collect elt))))
I can even use iterate with mapcar:
(iter (for list in lists)
      (mapcar #'(lambda (elt) (if elt (collect elt)))
              list))
This is the thing I most miss about loop macro, and I've ran into this kind of problem many times. Good thing that iterate is there ;)

Re: Better then loop/iterate?

@gugamilare: I agree that is annoying. It is a problem with not having a regular body as 'body', you either have to reinvent, or leave out simple macros like that.

I made a little project on Berlios I don't expect to be pushing the git much. The dayly tarball hasn't come yet, btw. I also added :initially, :finally. (And added setting what is returned earlier.)

Re: Better then loop/iterate?

Jasper wrote:
findinglisp wrote:Well, LOOP is just one big sexpr.
Well, without messing with the reader, i would have to be at least one, no?
Yes, that was my point. :D Often, people talk about loop like it isn't a sexpr at all. It is, of course, just one that doesn't have as much internal structure, but it can still be easily created and manipulated in macros and such.
I agree that much of my reaction to loop is esthetic/emotional, though. And i am also a novice in iterate. I know some real advantages though:
<<lots of good stuff deleted>>
Yup, I agree with most of that. Having COLLECT and friends be "embeddable" (for lack of a better word), would be very helpful. That's also one of my interests in ITERATE.

That said, LOOP is standard and documented, and you can always count on it being there. It certainly has limitations (the extensibility of ITERATE is very interesting, too), but a flawed standard is often better than a perfect extension in terms of creating a baseline for interoperability.
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Re: Better then loop/iterate?

findinglisp wrote:That said, LOOP is standard and documented, and you can always count on it being there. It certainly has limitations (the extensibility of ITERATE is very interesting, too), but a flawed standard is often better than a perfect extension in terms of creating a baseline for interoperability.
Well, I get your point, but don't completelly agree. Having one single implementation of iterate means it will work the same way everywhere. On the other hand, old implementations of clisp didn't support using loop keywords after do, like:
(loop for elt in list
     do (print elt)
     finally (return list))
I believe one "good" thing of loop being a standard is no dependencies. When I create a small library (like storable-functions), I don't like to make the user to install a library bigger than my library itself just to be "confortable". So I just use loop itself and, whenever I need to collect inside a mapcar, I let around a "with-collectors" macro (like arnesi's).

Re: Better then loop/iterate?

gugamilare wrote:
findinglisp wrote:That said, LOOP is standard and documented, and you can always count on it being there. It certainly has limitations (the extensibility of ITERATE is very interesting, too), but a flawed standard is often better than a perfect extension in terms of creating a baseline for interoperability.
Well, I get your point, but don't completelly agree. Having one single implementation of iterate means it will work the same way everywhere. On the other hand, old implementations of clisp didn't support using loop keywords after do, like:
(loop for elt in list
     do (print elt)
     finally (return list))
I believe one "good" thing of loop being a standard is no dependencies. When I create a small library (like storable-functions), I don't like to make the user to install a library bigger than my library itself just to be "confortable". So I just use loop itself and, whenever I need to collect inside a mapcar, I let around a "with-collectors" macro (like arnesi's).
I generally agree. I'd caution you on using the clisp experience as anything more than an indication that multiple implementations can read the same spec and simply implement things slightly differently. That is, it's really an example of the issues with a single-spec, multiple-implementation language than any issue with LOOP. If CL was a single-implementation language like Python (largely) or Perl or Ruby, then LOOP would just be LOOP and it would act however it acts.
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Re: Better then loop/iterate?

LOOP came up in a couple interesting talks at ILC09. In fact, the last ILC09 talk was on a semantically (and syntactically) improved descendent of LOOP.

"The Anatomy of a Loop: A Story of Scope and Control" by Olin Shivers
http://www.international-lisp-conferenc ... ivers_olin

The paper can be found at
http://www.ccs.neu.edu/home/shivers/citations.html

The ensuing discussion contained some interesting tidbits from "the old guard". Basically it sounded like "doing it right" would have prevented them from getting it accepted/done. So LOOP was accepted as-is, warts and all. In retrospect, that was the right decision -- Scheme still doesn't have a standard loop construct, and the CL standard was never revised.

In closing, if I were implementing a LOOP-killer (maybe called FROOT or TOGO), I'd start by implementing intermediate languages like LTK and CFG (see the paper). Then play around with the surface interface to see what suits your needs.

- Daniel

Re: Better then loop/iterate?

Those look like a good read, i will later.

As for iterate, i have read the code a little. Not sure if the codes approach is the best. (I'd have to read deeper to really make anything of judgement) Also why do people drown their code in comments :? use other files for documentation :(.. Not like we are all programming with only a text editor, we got browsers and all that shit on.. use it! For documentation how it works too. I usually only use single lines of documentations, unless where there are documentation-strings from which i want a good explanation.(I should check how to add/change doc-string after the function is defined.)

Another thing that struck me is this tidbit of the manual. From here.
what iterate doc about higher order functions wrote:One problem with higher-order functions is that they are inefficient, requiring multiple calls on their argument function. While the the built-ins, like map and mapcar, can be open-coded, that cannot be so easily done for user-written functions. Also, using higher-order functions often results in the creation of intermediate sequences that could be avoided if the iteration were written out explicitly.
Isn't that just untrue? If the functions are constant or based on higher-order functions on constants, you should be able to expand them just as macros, with similar results. What i actually consider to be the use for iterate, is that it is more convenient. Especially in iterating while accumulating/collecting it can be very handy.

Re: Better then loop/iterate?

Jasper wrote:
what iterate doc about higher order functions wrote:One problem with higher-order functions is that they are inefficient, requiring multiple calls on their argument function. While the the built-ins, like map and mapcar, can be open-coded, that cannot be so easily done for user-written functions. Also, using higher-order functions often results in the creation of intermediate sequences that could be avoided if the iteration were written out explicitly.
Isn't that just untrue?
Why do you think that this statement is not true?
Consider the following:
(reduce '+ (mapcar (lambda (x) (* x 2)) '(1 2 3 4)))
mapcar creates a new, freshly consed list. This list is the passed to reduce. But this fresh list is not neccessary, because it is possible to reduce this list in-place.
In Haskell, this statement about higher-order functions would be completely true: compiler would rearrange code to prevent unnecessary allocation of intermediate lists (this is called «list fusion»).

Re: Better then loop/iterate?

Reducing in place? Ok:
(reduce (lambda (x y) (+ x (* 2 y))) '(1 2 3 4) :initial-element 0)
what about this, then:
(let ((list '(1 2 3 4)))
  (reduce '+ (map-into list (lambda (x) (* x 2)) list)))
?

Re: Better then loop/iterate?

gugamilare wrote:Reducing in place? Ok:
(reduce (lambda (x y) (+ x (* 2 y))) '(1 2 3 4) :initial-element 0)
what about this, then:
(let ((list '(1 2 3 4)))
  (reduce '+ (map-into list (lambda (x) (* x 2)) list)))
?
Your function unnecessarily modifies the list. What I meant is more like this:
(let ((res 0)
  (map nil (lambda (x) (incf res (* x 2))) '(1 2 3 4)))
  res)
but writing such transformations by hand is tedious.

Re: Better then loop/iterate?

SERIES package does fusion/deforestation for Common Lisp, although no one seems to be actually using it, possibly because it seems very Haskellish way to code. Although as far as I know Haskell doesn't do fusion by default, at least not in stable GHC?

Re: Better then loop/iterate?

dmitry_vk wrote:but writing such transformations by hand is tedious.
This is lisp!
(define-compiler-macro reduce (&whole whole fun list &key (start 0))
  (cond
    ((listp list)
     (case (car list)
       (mapcar ;Kill redundant list creation.
	(destructuring-bind (fun slist) (cdr list)
	  (let ((res (gensym)))
	    `(let ((,res 0))
	       (map nil (lambda (x)
			  (setf ,res (funcall ,fun x)))
		    ,slist)
	       ,res))))
       (t
	whole)))
    (t
     whole)))

(defmacro reduce-c (fun list &key (start 0))
  (cond
    ((listp list)
     (case (car list)
       (mapcar ;Kill redundant list creation.
	(destructuring-bind (fun slist) (cdr list)
	  (let ((res (gensym)))
	    `(let ((,res 0))
	       (map nil (lambda (x)
			  (setf ,res (funcall ,fun x)))
		    ,slist)
	       ,res))))
       (t
	`(reduce ,fun ,list :start ,start))))
    (t
     `(reduce ,fun ,list :start ,start))))

(reduce '+ (mapcar (lambda (x) (* x 2)) '(1 2 3 4)))

(reduce-c '+ (mapcar (lambda (x) (* x 2)) '(1 2 3 4)))
This needs to incorporate more &key arguments, and more killing of intermediate lists, from other sources, but it can work. I don't think define-compiler-macro is the most powerful optimalization technique that can exist in principle, but it is pretty good. (Do any non-lisp languages even have it?) As for testing, the reduce-c macro-equivalent does seem to work.

BTW lets not forget:
nuntius wrote:"The Anatomy of a Loop: A Story of Scope and Control" by Olin Shivers
http://www.international-lisp-conferenc ... ivers_olin

The paper can be found at
http://www.ccs.neu.edu/home/shivers/citations.html

Re: Better then loop/iterate?

Jasper wrote:
dmitry_vk wrote:but writing such transformations by hand is tedious.
This is lisp!
...
This needs to incorporate more &key arguments, and more killing of intermediate lists, from other sources, but it can work. I don't think define-compiler-macro is the most powerful optimalization technique that can exist in principle, but it is pretty good. (Do any non-lisp languages even have it?) As for testing, the reduce-c macro-equivalent does seem to work.
I don't think that defining compiler-macros for functions from CL is a good thing, since it might break existing optimizations that rely on compiler-macros. Off-topic: it would be very nice to be able to define multiple compiler-macros for a single function.
As for other languages, Haskell (actually, GHC compiler) has rewrite rules that allow pattern-based rewriting of code.

Re: Better then loop/iterate?

Indeed, defining compiler macros for standard functions is classified by the ANSI as "unportable code". But reduce does accept a key argument, so:
(reduce #'+ '(1 2 3 4) :key (lambda (x) (* 2 x)))

Re: Better then loop/iterate?

Well, the point remains that the optimization is possible with compiler macros, and i think lisp implementations are a little thorough and effective at it relative to what i just thought of in 5 minutes.

Compiler macros can break eachother? Hmm, maybe compiler macros should have priorities, or multiple rounds of them. That way the implementations' optimizations are done first, and others can that go over what remains. (Anyway, if the function producing the intermediate list was a non-cl function, it shouldn't break anything.)

Of course packages should not be exporting stuff that is not the purpose of the package(for compiler macros, or anything else.), for instance PAL exports 'clamp'.. v+, v-, wish those were in separate packages. pal-vect, pal-util, or something. Pal is pretty good so-far though, albeit low on documentation(strings), maybe missing features. (But hey, add it yourself as you go :).)

Re: Better then loop/iterate?

Have you seen Lispbuilder-SDL? It is an interesting alternative, at least 4 games were created using it and it is pretty much stable.

Just to mention, you can check lispbuilder at sourceforge, but it is outdated. The first link is better.

Re: Better then loop/iterate?

@gugamilare, thanks, i am trying it. (Hrmm haven't gotten opengl to work, but my opengl seems to be bound to sdl, namespace interfering with that of lispbuilder, but i'll try figure it out more myself.)
Ramarren wrote:SERIES package does fusion/deforestation for Common Lisp, although no one seems to be actually using it, possibly because it seems very Haskellish way to code. Although as far as I know Haskell doesn't do fusion by default, at least not in stable GHC?
I read it a little, and i think that it is just because it is harder to learn, partially because it is further away from loop. (I think i encountered it before aswel.)
I also tried to read the thesis nuntius posted, but i don't think i have the background knowledge to understand properly. Is it graph theory that i am missing?

It might very well be that the better ways to loop are harder to learn. Maybe it would be good to make the other loop so that if you learn more, you are lead to the better ways to loop. For instance, by implementing the lesser ways to loop (Which could possibly be iterate-like macros) with the harder ways.

Last edited by Jasper on , edited 1 time in total.

Re: Better then loop/iterate?

Jasper wrote:I also tried to read the thesis nuntius posted, but i don't think i have the background knowledge to understand properly. Is it [url=http://en.wikipedia.org/wiki/Graph_theory]graph theory that i am missing?
s/posted/linked/

Yeah, Olin's paper isn't an easy read. From watching his talk, I gathered that it also requires a significant codebase to implement. I hope to tackle it someday (unless else someone gets there first), but I really linked it as a way of saying "this might finally be a solved problem". Once the CFG and LTK languages are implemented, making a nice API should be a walk in the park.

The funny notation starting around Figure 11 looks like sequent calculus.

Re: Better then loop/iterate?

Jasper wrote: Another thing that struck me is this tidbit of the manual. From here.
what iterate doc about higher order functions wrote:One problem with higher-order functions is that they are inefficient, requiring multiple calls on their argument function. While the the built-ins, like map and mapcar, can be open-coded, that cannot be so easily done for user-written functions. Also, using higher-order functions often results in the creation of intermediate sequences that could be avoided if the iteration were written out explicitly.
Isn't that just untrue? If the functions are constant or based on higher-order functions on constants, you should be able to expand them just as macros, with similar results. What i actually consider to be the use for iterate, is that it is more convenient. Especially in iterating while accumulating/collecting it can be very handy.
Looks fairly true to me. Which part do you think might not be true, the first statement about not being able to open-code user-written functions or the second part about creating intermediate sequences?
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Re: Better then loop/iterate?

nuntius wrote:Yeah, Olin's paper isn't an easy read. From watching his talk, I gathered that it also requires a significant codebase to implement.
Significant means a large amount of code? In my (limited) experience shorter is usually better, and longer often means going in the wrong direction. Of course, the short code one is looking for often is not easy to find. (And i might be wrong.)
findinglisp wrote:Which part do you think might not be true, the first statement about not being able to open-code user-written functions or the second part about creating intermediate sequences?
What is open-coding exactly(sorry :) :p ) whatever it is, inlining achieves it.(Right?) Most of the time when you use functions taking functions as arguments, you enter in constant functions, or functions from constant functions' output, or functions that depend on non-function variables.(Both can be expanded.) They can be replaced by their result, just like (+ (* 2 2) x) can be replaced with (+ 4 x). The only point where you can't expand functions is where they depend on non-constant functions(For which macros don't have a counterpart.) I am assuming that lisp actually does this. I could probably write code that does this. (If the functions are created with defun*, so i can store the functions and read their code.) However, if you ask me, it is the CL implementations responsibility, maybe i should look what optimizations implementations like SBCL say they do at this aspect. (Or *cringe* even look into their code.)
That said, the worry if the resulting code from expansion would become too large is different in macros relative to functions taking functions. In macros you already imagined what is going to happen, in functions you did not. However, it should be possible to estimate how much space expansion relative to function call costs, this is both an advantage and a disadvantage. The advantage is that it allows a little more choice whether you want to preserve space or speed, the disadvantage is that estimating this might not be that easy.

Maybe i should be more specific: The sequence to expand functions as function of functions:
  • Look for funcalls, where did the argument for the funcall come from?
  • A variable: See where variable came from, do one of below for what you find. (Might not be possible if turns out depending on non-constant functions, or if someone is iterating by changing a function or something like that, then again you can't compare with macros at that point; macros have no analogy except going through eval.) If used more then once, might want to expand into a local flet to let lisp decide whether to inline.
  • (funcall (lambda(,@args) ,@body) ,@got-> expand to (let (,@(combine args got)) ,@body), and then repeat the looking for funcalls.
  • (funcall (some-fun ,@args) ,@got) -> expand some-fun until you either find you can't, or find the final lambda. When latter do above.
  • (funcall #',fun-name ,@got) and (funcall ',fun-name ,@got) Easy, (,fun-name ,@got)
Edit: I forgot mentioning functions taking arguments inside the function to be inlined, you can inline those aswel, using the same method.
I guess i have not covered apply yet.


As for user-written intermediate sequences, i don't know of anything that can be done about that, besides define-compiler-macros specific to the functions. To be honest, functions taking functions as arguments and having as their way of output what they return strikes me as the wrong way to do it, except for very simple stuff like lists. If your program is a little more complicated, you have to think about how to collect the data to return together. It is like connecting each of the houses to the powerplant individually. You wouldn't have to do that if you just let the function return nothing(or maybe some information it stumbles upon and would be wasteful to waste.), letting the callbacks do the work like collection.

Re: Better then loop/iterate?

Jasper wrote:What is open-coding exactly(sorry :) :p ) whatever it is, inlining achieves it.(Right?)
Open-coding is like a very primitive form of inlining. The main difference is that it's performed directly by the compiler and thus can be even more efficient for certain operations. Essentially, the compiler is programmed to understand certain primitive functions (cons, car, cdr, length, etc.) and it just spits out the exact right machine code for those at the call site. Like inlining, it eliminates the function call, but unlike inlining, it doesn't rely on a bunch of compiler analysis to optimize things further. Because the compiler author knows exactly how standard primitives like car or length work, he can optimize them by hand when the compiler is written.
Maybe i should be more specific: The sequence to expand functions as function of functions:
  • Look for funcalls, where did the argument for the funcall come from?
  • A variable: See where variable came from, do one of below for what you find. (Might not be possible if turns out depending on non-constant functions, or if someone is iterating by changing a function or something like that, then again you can't compare with macros at that point; macros have no analogy except going through eval.) If used more then once, might want to expand into a local flet to let lisp decide whether to inline.
  • (funcall (lambda(,@args) ,@body) ,@got-> expand to (let (,@(combine args got)) ,@body), and then repeat the looking for funcalls.
  • (funcall (some-fun ,@args) ,@got) -> expand some-fun until you either find you can't, or find the final lambda. When latter do above.
  • (funcall #',fun-name ,@got) and (funcall ',fun-name ,@got) Easy, (,fun-name ,@got)
Edit: I forgot mentioning functions taking arguments inside the function to be inlined, you can inline those aswel, using the same method.
I guess i have not covered apply yet.
Most compilers already perform various transformations like this. Basic constant expressions are typically evaluated during compilation (e.g., things like (+ 2 2) is simply replaced with 4). This is not, however, what Olin was talking about. There are many cases when the call site cannot be optimized because you don't know what to inline within it. For instance, if I write (mapcar #'xyzzy '(1 2 3)), and xyzzy is defined in another compilation unit, the compiler, even a very smart compiler, has no chance to optimize anything. While a good compiler can open-code the instructions for mapcar (essentially doing the equivalent of (loop for temp in '(1 2 3) collect (funcall #'xyzzy temp)) ), it still must call xyzzy each time. If xyzzy is in the same compilation unit and it is declared to be inline, then perhaps a smart compiler will inline it in this instance, but realize that inline directives are hints, not orders. A compiler is not required to inline anything, no matter how many inline and speed directives you give it.
As for user-written intermediate sequences, i don't know of anything that can be done about that, besides define-compiler-macros specific to the functions. To be honest, functions taking functions as arguments and having as their way of output what they return strikes me as the wrong way to do it, except for very simple stuff like lists. If your program is a little more complicated, you have to think about how to collect the data to return together. It is like connecting each of the houses to the powerplant individually. You wouldn't have to do that if you just let the function return nothing(or maybe some information it stumbles upon and would be wasteful to waste.), letting the callbacks do the work like collection.
This is the kind of thing that Shivers was talking about, I think. There are many cases where the simple, obvious, static analysis falls short. Among these cases are many interesting ones (not strange, theoretical corner cases, for instance, but real practical problems). Devices like Common Lisp compiler macros were created, I think, to allow application writers to provide application-specific smarts to a standard compiler to help it along through some of the optimizations. While we can always theorize that a compiler should be able to do such and such, some of the analysis can get very complex very quickly and may be very error prone to handle the general cases. Many compilers fall well short of what might be theoretically possible.
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Re: Better then loop/iterate?

I have been a little annoyed by this: Having flets, lets, macrolets and symbol-macrolets separate all the time has disadvantages.

1) More nested then need be, implying more parenthesis, more depth of indentation.

2) Sometimes need lets or flets in some specific order, causing more nesting.

So it might be better to use macros that create all these macros at the same time, and it seems to be the only disadvantage of that is that it punishes the user a little less when he doesn't create sub functions fast enough. I don't think that is worth it; programmers need to recognize that themselves anyway.

There are a bunch of ways to do this:
  • With a macro with a specific variable(of any *let) creation area. The umac this thread was begun with somewhat does that.(Looking at the source code, i see i didn't write it so it can fix (2) currently, easily fixed, and then it'll do it.) Advantages is clear distinction between variables and body, and separate namespace for 'macros to create variables'.(def-umac) You could make a with-slots for the umac, for instance. (maybe not so inferior to iterate after all.)
  • An iterate-like approach, working in a scope-like manner. here the advantage is that regular macros can make variables. It likely needs a 'scope-transparant'
  • Loop like approach, much like the iterate approach, but just with symbols(which also need to indicate regular body elements). Don't really like it. Nor do i know how to make it properly flexible. WITH somewhat does this.
I feel i am missing something.. Maybe a 'denesting' function..
(defmacro denest ((&rest args) &body body)
  (if (null args)
    `(progn ,@body)
    `(,@(car args)
      (denest (,@(cdr args)) ,@body))))
Edit: maybe nestedness really is the problem loop, iterate and such are trying to solve, i think this thing will perform at least just as well as umac in looping. At least accumulators are made easily enough:
(defmacro summing ((&optional (initial 0) (onto (gensym))) &body body)
  `(let ((,onto ,initial))
     (macrolet ((sum (&rest summed)
		 (list 'setf ',onto (append (list '+ ',onto) summed)))) ;Too lazy to figure out how to backquote here.
       ,@body)
     ,onto))

(denest ((summing ())
	      (dolist (el (list 1 2 3 4))))
  (sum el))

Last edited by Jasper on , edited 1 time in total.

Re: Better then loop/iterate?

Jasper wrote:I have been a little annoyed by this: Having flets, lets, macrolets and symbol-macrolets separate all the time has disadvantages.

1) More nested then need be, implying more parenthesis, more depth of indentation.

2) Sometimes need lets or flets in some specific order, causing more nesting.

So it might be better to use macros that create all these macros at the same time, and it seems to be the only disadvantage of that is that it punishes the user a little less when he doesn't create sub functions fast enough. I don't think that is worth it; programmers need to recognize that themselves anyway.

There are a bunch of ways to do this:
  • With a macro with a specific variable(of any *let) creation area. The umac this thread was begun with somewhat does that.(Looking at the source code, i see i didn't write it so it can fix (2) currently, easily fixed, and then it'll do it.) Advantages is clear distinction between variables and body, and separate namespace for 'macros to create variables'.(def-umac) You could make a with-slots for the umac, for instance. (maybe not so inferior to iterate after all.)
  • An iterate-like approach, working in a scope-like manner. here the advantage is that regular macros can make variables. It likely needs a 'scope-transparant'
There is a metabang-bind project. It does what you describe and is extensible.

Re: Better then loop/iterate?

Looking at metabang bind, not exactly. I don't really see the extension mechanism in the docs. I suppose you can add different keywords, just like :values was added. If it works that way, it's somewhat similar to umac i guess. Likely with a better implementation of it too. So you are probably right, but the docs i linked to would be a little out of date in that case..

Weird that i didn't think of something as simple as denest, it's five lines.. I am stumped... I suspect it can do pretty much everything loop(edit or iterate) can(at least everything i regularly use, which is admittedly, not that much), using macros that do the various things, and which can be used stand-alone too. It would use only a few parenthesis(and no nesting) more then macros like iterate. Further, it feeds from about every macro it encounters. It's not a project.. it's just there.. wtf.

Plus, a minor adjustment removes those few more parenthesis.
(defmacro denest* ((&rest args) &body body)
  (if (null args)
    `(progn ,@body)
    `(,(caar args) (,@(cdar args)) ;Edit, made a silly mistake, forgot to change this to denest* too.
      (denest*(,@(cdr args)) ,@body))))
Do mind though that now it can't use with-slots, multiple-value-bind anymore.

Edit: did i edit on the denest macro? or did you just happen to not quote it. I remember not editing it on, but just submitting it. Memory might not serve me right.

Last edited by Jasper on , edited 1 time in total.

Re: Better then loop/iterate?

If you look at metabang-bind's code, you'll see it is extensible, and also support for much more keywords than specified in the docs.

Re: Better then loop/iterate?

Jasper wrote: 2) Sometimes need lets or flets in some specific order, causing more nesting.
Why don't you use LET* and LABELS then?
"Just throw more hardware at it" is the root of all evil.
Svante

Re: Better then loop/iterate?

LABELS somehow didn't get on my radar.. Why isn't it called FLET* -_-. Either way, LET* and LABELS don't solve the mixing of functions and variables. Nor for WITH-SLOTS, DOLIST, really pretty much anything. Actually Neither does DENEST, but DENEST fixes the accompanying nesting+indentation needed.

METABANG-BIND might be able to work it with DEFBINDING-FORM. However, look at how it does :slots, for instance, compared with this macro you just plug the existing macro in. The core part also is more then 5 lines.

I am not saying this macro is something to get exited over, it can't do wonders. But it seems a little surprising that there is such a simple yet powerful method that nobody seems to have been using. I have made a little modified method, to add denest-specific 'keyword macros', that work exactly the same as macros, they're just there to use less namespace. And i added two block types to return from 'denest' to drop out completely, and 'denest-prev-ret', the latter is to return whatever the accumulators/value returning macros have been picking up. totally ~60 lines of code with all that.(With doc strings and all, and including DENEST*) Totalling 183 lines, with various accumulators, iterators and returning thingies. With this, code like this works.
(denest ((collecting (nil list))
  	      (summing (0 a))
 	       (:return (values a list)) ;Override return of summing. (summing overrided that of collecting) does (return-from denest value)
	       (dolist (el (list 1 2 3 4))))
  (summing el)
  (collecting a))

(denest ((collecting ())
	       (:integer-block ((i 0 10) (j 0 10)) ()))
  (when (and (= i 5) (= j 5))
    (finish)) ;finish does (return-from denest-prev-ret), which then returns whatever that macro intended to return. (Unfortunately requires a block for that.)
  (collecting (list i j)))
As i said, it won't suddenly make things easy, and god knows you've never seen me output anything useful(yet), but it does seem to me like an improvement. Unlike umac, i think i'll opine it better then iterate. I can hardly believe no-one else has come up with this, probably it hasn't.. But why are we using stuff like LOOP, ITERATE then?

Re: Better then loop/iterate?

dmitry_vk wrote:
Jasper wrote:I have been a little annoyed by this: Having flets, lets, macrolets and symbol-macrolets separate all the time has disadvantages.

1) More nested then need be, implying more parenthesis, more depth of indentation.

2) Sometimes need lets or flets in some specific order, causing more nesting.

So it might be better to use macros that create all these macros at the same time, and it seems to be the only disadvantage of that is that it punishes the user a little less when he doesn't create sub functions fast enough. I don't think that is worth it; programmers need to recognize that themselves anyway.

There are a bunch of ways to do this:
  • With a macro with a specific variable(of any *let) creation area. The umac this thread was begun with somewhat does that.(Looking at the source code, i see i didn't write it so it can fix (2) currently, easily fixed, and then it'll do it.) Advantages is clear distinction between variables and body, and separate namespace for 'macros to create variables'.(def-umac) You could make a with-slots for the umac, for instance. (maybe not so inferior to iterate after all.)
  • An iterate-like approach, working in a scope-like manner. here the advantage is that regular macros can make variables. It likely needs a 'scope-transparant'
There is a metabang-bind project. It does what you describe and is extensible.
I guess mine in better: http://github.com/ks/X.LET-STAR ;)

Re: Better then loop/iterate?

I don't think your approach is better.(although the code is good!) The way Metabang bind and let-star both are capable of what denest does seems to expose to me that denesting is the better route.

If let-star exposed define-binder, then you could make one that looks a little like denest:
(define-binder (:macro (name symbol) args decls body)
  `(,name ,@args ,@body))
And of course you can 'import' macros to various symbols, stuffing the arguments into the name and args. (And metabang bind probably can do it too, but i haven't checked.)

A few disadvantages of define-binder versus denest:
  • You have to explicitly 'import' macros.
  • define-binder requires more learning then just making a regular macro and plugging it into denest, or using def-denest-macro for utilizing the keywords also. (def-denest-macro works just like defmacro, except the name must be a keyword, and it only works in denest or when using it via use-denest-macro.
  • The arguments for let-star are limited to the form (var val) (Rather annoying when using the :macro like that.)
Note that i made a little alteration to the basic idea of denest.
(defmacro denest (&rest forms)
  (if (null (cdr forms))
    (car forms)
    `(,@(car forms) (denest ,@(cdr forms)))))
PS: i'd have updated autodocs, git and little website for this, but damn berlios.de is down, i think i am changing 'home'. (Or maybe my own website..) Github good?

PS2: Use doc-strings?! (documentation is setfable, if you don't want it in the code.) and why make parse-binding a flet? You can just not export it too. (Like you did with process-lambda-list-with-ignore-markers)