From: Drew McDermott
Subject: Crazy macro
Date: 
Message-ID: <1146327686.463468.103010@j33g2000cwa.googlegroups.com>
I have invented a macro that I believe is fairly novel, and have become
somewhat fond of.  On the other hand, the macro is a little weird.  I
present it to the world to see if anyone thinks it should be strangled
in the crib or allowed to flourish.

The problem the macro addresses is the 'Vietnamization" of Lisp code,
the tendency of large programs to wander off to the right as
indentation gets deeper and deeper.  Many people advocate
breaking large programs into little functions to avoid such problems,
but I resist that.  For one thing, it creates a lot of functions with
no real reason for existence.  For another, if the program's current
state is stored in several local variables, then they must all be
passed as arguments to the little functions, and many must be returned
as values.  Finally, if one's code is well commented, so that each
segment is understandable, I don't see any reason why the absence of
function boundaries at the segments should freak out someone reading
it.

So, we need a way to avoid Vietnamization while preserving the fact
that we're reading a single piece of code.  Here's where the macro
comes in.  It's called 'control-nest.'  A call looks like this:

   (control-nest
    :tag1
      exp1[:tag2]
    :tag2
      exp2[:tag3]
    ...
    :tagN
      expN)

Each expI should contain exactly one occurrence of tag{I+1}, except for
expN.  Writing expI(e) to means expI with tag{I+1} replaced by e, the
call to 'control-nest' expands to
     exp1(exp2(...expN))

As an example, we can use 'control-nest' to provide the functionality
of 'let*' for 'multiple-value-let':

   (control-nest
      (multiple-value-let (x y z)
                          (foo ...)
         :bind-p-q)
    :bind-p-q
      (multiple-value-let (p q)
                          (baz x y)
         :bind-r-s)
    :bind-r-s
      (multiple-value-let (r s)
                          (zap x p)
         ...)))

expands to

   (multiple-value-let (x y z)
                       (foo ...)
      (multiple-value-let (p q)
                          (baz x y)
         (multiple-value-let (r s)
                             (zap x p)
            ...)))

(Notice that we didn't provide 'tag1' here; it's optional because it
plays no actual role in expanding the macro.)

Any comments?

By the way, this macro is included in YTools
(http://www.cs.yale.edu/homes/dvm/#software).

   -- Drew McDermott
      Yale University

From: Ron Garret
Subject: Re: Crazy macro
Date: 
Message-ID: <rNOSPAMon-72F95A.13195529042006@news.gha.chartermi.net>
In article <························@j33g2000cwa.googlegroups.com>,
 "Drew McDermott" <········@bluebottle.com> wrote:

> I have invented a macro that I believe is fairly novel, and have become
> somewhat fond of.  On the other hand, the macro is a little weird.  I
> present it to the world to see if anyone thinks it should be strangled
> in the crib or allowed to flourish.
> 
> The problem the macro addresses is the 'Vietnamization" of Lisp code,
> the tendency of large programs to wander off to the right as
> indentation gets deeper and deeper.  Many people advocate
> breaking large programs into little functions to avoid such problems,
> but I resist that.  For one thing, it creates a lot of functions with
> no real reason for existence.  For another, if the program's current
> state is stored in several local variables, then they must all be
> passed as arguments to the little functions, and many must be returned
> as values.  Finally, if one's code is well commented, so that each
> segment is understandable, I don't see any reason why the absence of
> function boundaries at the segments should freak out someone reading
> it.
> 
> So, we need a way to avoid Vietnamization while preserving the fact
> that we're reading a single piece of code.  Here's where the macro
> comes in.  It's called 'control-nest.'  A call looks like this:
> 
>    (control-nest
>     :tag1
>       exp1[:tag2]
>     :tag2
>       exp2[:tag3]
>     ...
>     :tagN
>       expN)
> 
> Each expI should contain exactly one occurrence of tag{I+1}, except for
> expN.

In that case the tags are redundant.  All you need is a privileged 
symbol (\... would seem like a natural choice) to indicate that it is to 
be replaced by the subsequent form, e.g.:

(control-nest
  (multiple-value-let (x y) (frob) \...)
  (multiple-value-let (q z) (blurp) (foo \...))
  etc...
)

It would be nice to be able to do something like this:

(control-nest
  (multiple-value-let (x y) (foo) (if (condition) :then-tag :else-tag))
  :then-tag (then-code)
  :else-tag (else-code))

but the problem with that is it breaks up your symbol space into those 
symbols you can return from a control-nest and those which serve as 
tags, which rubs me the wrong way.  It makes it hard to find bugs like:

(control-nest
  (multiple-value-let (x y) (foo) (if (condition) :than-tag :else-tag))
  :then-tag (then-code)
  :else-tag (else-code))

My preferred design would use a second form to indicate when something 
is a tag, e.g.:

(control-nest
  (multiple-value-let (x y) (foo)
    (if (condition) (cont then) (cont else)))
  then (then-code ... (cont c1))
  else (else-code ... (cont c2))
  c1 ...
  c2 ...)

or something like that.

rg
From: Drew McDermott
Subject: Re: Crazy macro
Date: 
Message-ID: <1146368968.799639.60160@i39g2000cwa.googlegroups.com>
> In that case the tags are redundant.  All you need is a privileged
> symbol (\... would seem like a natural choice) to indicate that it is to
> be replaced by the subsequent form, e.g.:
>
> (control-nest
>   (multiple-value-let (x y) (frob) \...)
>   (multiple-value-let (q z) (blurp) (foo \...))
>   etc...
> )

True, but the tags can be quite mnemonic.  For instance, suppose you
have what is conceptually a conjunction of conditions to test, but
because of ancillary variable binding you wind up with let's and
cond's cluttering the code up.  Then you could write

(control-nest
   (let ((...))
      (cond ((unusual-case)
             (bail))
            (t
             :and)))
 :and
   (multiple-value-let (...)
      (cond ((check-next-condition)
             :and)
            (t
             (bomb))))
 :and
 ...)

More usually, the tags can indicate the next phase.  For example, in a
grading program, the macro got used thus (where I've deleted everything

that doesn't contribute to the nesting):

       (control-nest
          (let (...)
             [... ]
             (cond ((or (> group-size 0)
                        avoid-empty-grade-pop*)
                    :collect-next-group)))

        :collect-next-group
          (let (...)
             [...]
             (multi-let (((group group-weight rem-students rem-weights)
                          (weighted-take num-to-take
                                         to-be-curved
                                         remaining-weights)))
                [...]
                (cond ((not (null group))
                       [...]
                       :collect-squeakers))))

        :collect-squeakers
          ;; Add to group those who missed the bottom by a fraction
          ;; of a point
          (let* (...)
             [...]
             (let (...)
                [...]
                (let* ((num-squeakers (len squeakers))
                       (squeakers-weight
                           (<< + (take num-squeakers
                                            remaining-weights))))
                   [...]
                   (cond ((not (null squeakers))
                          [...]))))))

You see why Vietnamization is a problem in my code!

                                                -- Drew
From: Ron Garret
Subject: Re: Crazy macro
Date: 
Message-ID: <rNOSPAMon-ED65E5.22411429042006@news.gha.chartermi.net>
In article <·······················@i39g2000cwa.googlegroups.com>,
 "Drew McDermott" <········@bluebottle.com> wrote:

> > In that case the tags are redundant.  All you need is a privileged
> > symbol (\... would seem like a natural choice) to indicate that it is to
> > be replaced by the subsequent form, e.g.:
> >
> > (control-nest
> >   (multiple-value-let (x y) (frob) \...)
> >   (multiple-value-let (q z) (blurp) (foo \...))
> >   etc...
> > )
> 
> True, but the tags can be quite mnemonic.

That's what comments are for IMHO.

I could see tags being useful in a situation like this:

(cond ((condition1) :body1))
      ((condition2) :body2))
:body1 (body1)
:body2 (body2)

But as you've described it you can't do that.

> You see why Vietnamization is a problem in my code!

Personally I deal with this by transforming code like this:

(cond ((condition1)
       (let (...) ...))
      (t (foo)))

Into this:

(if (not (condition1)) (return (foo))
(let (...) ...)

rg
From: Ken Tilton
Subject: Re: Crazy macro
Date: 
Message-ID: <07O4g.19$pP6.1@fe12.lga>
Drew McDermott wrote:
> As an example, we can use 'control-nest' to provide the functionality
> of 'let*' for 'multiple-value-let':
> 
>    (control-nest
>       (multiple-value-let (x y z)
>                           (foo ...)
>          :bind-p-q)
>     :bind-p-q
>       (multiple-value-let (p q)
>                           (baz x y)
>          :bind-r-s)
>     :bind-r-s
>       (multiple-value-let (r s)
>                           (zap x p)
>          ...)))
> 
> expands to
> 
>    (multiple-value-let (x y z)
>                        (foo ...)
>       (multiple-value-let (p q)
>                           (baz x y)
>          (multiple-value-let (r s)
>                              (zap x p)
>             ...)))
> 
> (Notice that we didn't provide 'tag1' here; it's optional because it
> plays no actual role in expanding the macro.)
> 
> Any comments?

(a) I have the rope, where is the crib?
(b) 17" flat panel monitors, bigger if you like
(c) FLET, LABELS
(d) You call that readable?!
(e) finally...

> For another, if the program's current
> state is stored in several local variables

This may be The Real Problem. A more functional attitude would clean up 
your code nicely, making all this unnecessary.

hth, kenny
From: Drew McDermott
Subject: Re: Crazy macro
Date: 
Message-ID: <1146369593.123248.167640@j33g2000cwa.googlegroups.com>
> This may be The Real Problem. A more functional attitude would clean up
> your code nicely, making all this unnecessary.

I have a very functional attitude, as far as I know.  I think the
issues are orthogonal.  When I used the word "state" I didn't mean it
in the way you think (but I can't really think of a better term).
Suppose you are computing N intermediate values on the way to a final
result.  You need to store them in local variables, but that doesn't
mean you need assignment statements.  If you insert a subroutine just
to break up the flow, you're going to have to write something like

    (multiple-value-let (v1 ... vN)
                              (sub v1 ... vN)
       ... resume ...)

or perhaps

   (multiple-value-call #'sub2 (sub1 v1 .... vN))

This is all perfectly functional, and perfectly hideous.

    -- Drew
From: Ken Tilton
Subject: Re: Crazy macro
Date: 
Message-ID: <GsX5g.1891$8A2.1703@fe10.lga>
Drew McDermott wrote:
>>This may be The Real Problem. A more functional attitude would clean up
>>your code nicely, making all this unnecessary.
> 
> 
> I have a very functional attitude, as far as I know.  I think the
> issues are orthogonal.  When I used the word "state" I didn't mean it
> in the way you think (but I can't really think of a better term).
> Suppose you are computing N intermediate values on the way to a final
> result.  

The programming just sounds too hairy. Forget about functional or not, 
maybe it is insufficiently OO. I do not know, and know one knows, 
because you are not showing us the code. You are just showing us the 
macrology and asking us what we think.

Me, working backwards, I think your code could be cleaner. Why are these 
values stopping off at so many local variables and then being herded 
into other functions? Just sounds hairy. And apparently this happens so 
often and to such a degree of indentation severity that you took the 
trouble to cook up a rather bizarre macro.

Graham talked about the way code should look, the left-right downslope 
thing. i think better code arises by targeting such an appearance, as 
strange as that sounds. And, picturing your code, it seems to me you 
would do better by making your macro unnecessary.

just my 2, since you asked. Well, you did not ask for your coding style 
to be dragged into this, but it is, isn't it?

kenny

-- 
Cells: http://common-lisp.net/project/cells/

"Have you ever been in a relationship?"
    Attorney for Mary Winkler, confessed killer of her
    minister husband, when asked if the couple had
    marital problems.
From: Pascal Bourguignon
Subject: Re: Crazy macro
Date: 
Message-ID: <87odyk5no2.fsf@thalassa.informatimago.com>
"Drew McDermott" <········@bluebottle.com> writes:
> [...]
> So, we need a way to avoid Vietnamization while preserving the fact
> that we're reading a single piece of code.  Here's where the macro
> comes in.  It's called 'control-nest.'  A call looks like this:
>
>    (control-nest
>     :tag1
>       exp1[:tag2]
>     :tag2
>       exp2[:tag3]
>     ...
>     :tagN
>       expN)

(tagbody
 :tag1
   exp1[(go :tag2)]
 :tag2
   exp2[(go :tag3)]
 ...
 :tagN
   expN)

Did you really need a new macro? What do you win? Avoiding to write (GO )?
 

Recently on cll, we presented a macro with something more than a mere
tagbody:  in addition to the control flow, it links the data flow.

http://groups.google.com/group/comp.lang.lisp/browse_frm/thread/a8c3d95939adc6aa/a712e4d464bb9945?lnk=st&q=functional-pipe+group%3Acomp.lang.lisp+author%3APascal+author%3ABourguignon&rnum=1&hl=en#a712e4d464bb9945

  (functional-pipe
    (lambda (item) (elementp x (funcall neighborhood item)))
    (print :hi)
    (remove-if-not ** items)
    (mapcar neighborhood *)
    (reduce (function intersection) *)))

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we. -- Georges W. Bush
From: Drew McDermott
Subject: Re: Crazy macro
Date: 
Message-ID: <1146330501.686671.9160@j33g2000cwa.googlegroups.com>
It's not the same as GOing!  All the bindings established by earlier
'exp's are still in place while evaluating later ones.  

   -- Drew
From: Pascal Bourguignon
Subject: Re: Crazy macro
Date: 
Message-ID: <87k6985lbl.fsf@thalassa.informatimago.com>
"Drew McDermott" <········@bluebottle.com> writes:
> It's not the same as GOing!  All the bindings established by earlier
> 'exp's are still in place while evaluating later ones.  

Sorry, I overlooked that.   
Then indeed, it is interesting, if you write long functions.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"Do not adjust your mind, there is a fault in reality"
 -- on a wall many years ago in Oxford.
From: Coby Beck
Subject: Re: Crazy macro
Date: 
Message-ID: <62R4g.2475$zn1.196@edtnps90>
"Drew McDermott" <········@bluebottle.com> wrote in message 
·····························@j33g2000cwa.googlegroups.com...
>I have invented a macro that I believe is fairly novel, and have become
> somewhat fond of.  On the other hand, the macro is a little weird.  I
> present it to the world to see if anyone thinks it should be strangled
> in the crib or allowed to flourish.
>
> The problem the macro addresses is the 'Vietnamization" of Lisp code,
> the tendency of large programs to wander off to the right as
> indentation gets deeper and deeper.  Many people advocate
> breaking large programs into little functions to avoid such problems,
> but I resist that.  For one thing, it creates a lot of functions with
> no real reason for existence.  For another, if the program's current
> state is stored in several local variables, then they must all be
> passed as arguments to the little functions, and many must be returned
> as values.  Finally, if one's code is well commented, so that each
> segment is understandable, I don't see any reason why the absence of
> function boundaries at the segments should freak out someone reading
> it.

I was ready to reply by here that you should use local functions, this is 
IMO exactly what they are there for.  But I continued reading anyway...

> So, we need a way to avoid Vietnamization while preserving the fact
...
>   (control-nest
>      (multiple-value-let (x y z)
>                          (foo ...)
>         :bind-p-q)
>    :bind-p-q
>      (multiple-value-let (p q)
>                          (baz x y)
>         :bind-r-s)
>    :bind-r-s
>      (multiple-value-let (r s)
>                          (zap x p)
>         ...)))
>
> expands to
>
>   (multiple-value-let (x y z)
>                       (foo ...)
>      (multiple-value-let (p q)
>                          (baz x y)
>         (multiple-value-let (r s)
>                             (zap x p)
>            ...)))
>

I stand by my knee-jerk reaction.

(flet ((doit ()
         (lots of non-overindented stuff)
         (all of it using x y z p q r s)))
    (multiple-value-let (x y z)
                       (foo ...)
      (multiple-value-let (p q)
                          (baz x y)
         (multiple-value-let (r s)
                             (zap x p)
            (doit))))


I know flet already introduces alot of indentation, it might be acceptable 
with a lot of local functions to do

(flet
   ((foo ()
       ...)
    (bar ()
       ...))
 ...)

My 2cents...

-- 
Coby Beck
(remove #\Space "coby 101 @ bigpond . com")
From: Drew McDermott
Subject: Re: Crazy macro
Date: 
Message-ID: <1146370395.118681.295050@i40g2000cwc.googlegroups.com>
As far as I can see, the only way you gain from using local functions
is if you use shared variables to avoid passing and returning values:

(let ((shared1...) (shared2 ...))
   (flet ((fn1 (...)
             (let ()
                (cond (..
                       (fn2 ...)))))
          (fn2 (...)
              (cond (...)
                    (t
                     (multi-let ((..))
                        (fn3 ...)))))
          ...)

      (fn1 ...)))

where the functions can communicate by setting and reading the sharedI.

I'll let you and Kenny argue this one out.

By the way, I've used all the ideas you guys have suggested for years.
I only stumbled on the idea of 'control-nest' in the last six months.
In general, I would rather find a tidy way to break a long computation
into local subroutines.  But if there's really no natural way to do
it, control-nest is a nice resource to have.  It's cheaper than buying
the extra-wide monitor.

                                             -- Drew
From: Barry Margolin
Subject: Re: Crazy macro
Date: 
Message-ID: <barmar-0EA35F.00301130042006@comcast.dca.giganews.com>
In article <························@j33g2000cwa.googlegroups.com>,
 "Drew McDermott" <········@bluebottle.com> wrote:

> As an example, we can use 'control-nest' to provide the functionality
> of 'let*' for 'multiple-value-let':
> 
>    (control-nest
>       (multiple-value-let (x y z)
>                           (foo ...)
>          :bind-p-q)
>     :bind-p-q
>       (multiple-value-let (p q)
>                           (baz x y)
>          :bind-r-s)
>     :bind-r-s
>       (multiple-value-let (r s)
>                           (zap x p)
>          ...)))

The thing that bothers me about this is the loss of referential 
transparency.  To understand what X and Y mean in the :BIND-P-Q section, 
you have to find all the places where the tag is referenced.  This is 
similar to the reason why lexical binding is generally preferred over 
dynamic binding.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
From: Drew McDermott
Subject: Re: Crazy macro
Date: 
Message-ID: <1146622539.611458.45460@j33g2000cwa.googlegroups.com>
>The thing that bothers me about this is the loss of referential
> transparency.  To understand what X and Y mean in the
> :BIND-P-Q section, you have to find all the places where the tag > is referenced.

There's only one.

     -- Drew
From: Barry Margolin
Subject: Re: Crazy macro
Date: 
Message-ID: <barmar-7660C6.01164803052006@comcast.dca.giganews.com>
In article <·······················@j33g2000cwa.googlegroups.com>,
 "Drew McDermott" <········@bluebottle.com> wrote:

> >The thing that bothers me about this is the loss of referential
> > transparency.  To understand what X and Y mean in the
> > :BIND-P-Q section, you have to find all the places where the tag > is 
> > referenced.
> 
> There's only one.

OK, then it just raises my "lack of generality" alarm.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
From: Wade Humeniuk
Subject: Re: Crazy macro
Date: 
Message-ID: <sP35g.2836$Yy5.1711@edtnps89>
I prefer something like this,

(defmacro multiv-let* (vars inits &body body)
   (let ((form `(progn ,@body)))
     (loop for init in (reverse inits)
           for vars in (reverse vars)
           do
           (setf form `(multiple-value-bind ,vars ,init ,form)))
     form))

Then

(multiv-let* ((x y z) (p q) (r s))
     ((foo) (baz x y) (zap x p))
   (stuff))

Expands to

(MULTIPLE-VALUE-BIND (X Y Z)
     (FOO)
   (MULTIPLE-VALUE-BIND (P Q)
       (BAZ X Y)
     (MULTIPLE-VALUE-BIND (R S) (ZAP X P) (PROGN (STUFF)))))

In general the multi-lets(*) could be written to accept normal,
multiple and destructured binding with a little syntax.

Wade
From: Larry Clapp
Subject: Re: Crazy macro
Date: 
Message-ID: <slrne5jmr0.pnr.larry@theclapp.ddts.net>
On 2006-04-29, Drew McDermott wrote:
> I have invented a macro that I believe is fairly novel, and have
> become somewhat fond of.  On the other hand, the macro is a little
> weird.  I present it to the world to see if anyone thinks it should
> be strangled in the crib or allowed to flourish.

Random thoughts ...

o I've read the entire thread so far.  It seems to me you've
  re-invented one facet of literate programming: having one piece of
  code contain or be contained by some other code some unknown (but in
  your case probably short) distance away.  If *I* wanted that, I'd
  just use noweb or the Leo editor.

o I read once what I consider a good definition of bad design:

  1. It is hard to change because every change affects too many other
     parts of the system.
  2. When you make a change, unexpected parts of the system break.
  3. It is hard to reuse in another application because it cannot be
     disentangled from the current application.

  -- The Dependency Inversion Principle, Robert C. Martin

  I think using your macro would make your code suffer only a little
  from #1, could suffer quite a lot (locally) from #2, and some from
  #3.  I could be wrong.

o I think I would have a harder time refactoring code inside this
  macro than equivalent nested code.

o Lots of people talk about Lisp and say "after a while, you stop
  seeing the parentheses and just read the indentation."  You've
  broken that.  Lots of people tell their editors "go up N levels of
  blocks/parens/what-have-you".  You've broken that (unless you teach
  your editor about your macro).

o Lots of coding has trained my intuition to expect code close to the
  left margin to not depend on very much.  You've broken that.  (E.g.
  you could have code all but flush-left, but it's nevertheless inside
  a cond in a loop in an let.)

I dunno.  Enough bugs me about your macro that I don't think I'd want
to try it to see if I'm wrong.

That said, by all means, let it flourish.  But if you come back to
code a year later and have to macroexpand it to figure out wtf it's
doing, and abandon it, please report back.

-- Larry
From: Drew McDermott
Subject: Re: Crazy macro
Date: 
Message-ID: <1146922229.370089.132130@i40g2000cwc.googlegroups.com>
> [Larry Clapp]
> Random thoughts ...

Actually, I like a lot of your random thoughts.

> o I've read the entire thread so far.  It seems to me you've
>   re-invented one facet of literate programming: having one piece of
>   code contain or be contained by some other code some unknown (but in
>   your case probably short) distance away.  If *I* wanted that, I'd
>   just use noweb or the Leo editor.

That would be rather drastic.  I've actually played a lot with
literate programming in the past couple of years.  I even wrote a
general-purpose literate-programming system (in Lisp, of course).  Not
a very good one.  I have an essay on why on balance literate
programming is not a very good idea which I may polish off and publish
at some point.

> o I read once what I consider a good definition of bad design:
>
>   1. It is hard to change because every change affects too many other
>      parts of the system.
>   2. When you make a change, unexpected parts of the system break.
>   3. It is hard to reuse in another application because it cannot be
>      disentangled from the current application.
>
>   -- The Dependency Inversion Principle, Robert C. Martin
>
>   I think using your macro would make your code suffer only a little
>   from #1, could suffer quite a lot (locally) from #2, and some from
>   #3.  I could be wrong.

I have a lot of trouble with all these Principles, Patterns,
Frameworks, etc. that are all the rage these days.

> o I think I would have a harder time refactoring code inside this
>   macro than equivalent nested code.

If by "refactor" you mean "rewrite," you may have a point.  Otherwise
I am confused.  I have struggled to understand what the term means,
and finally decided it meant "reimplement (while leaving the behavior
the same)."  So _code_ isn't refactored; systems are.

But code is rewritten, and you're right that 'control-nest' seems to
work best when tidying up code I'm pretty sure about.

> o Lots of people talk about Lisp and say "after a while, you stop
>   seeing the parentheses and just read the indentation."  You've
>   broken that.

Yes and no.  The basic breakage is no worse than for 'labels', and
it's probably more readable than 'labels' because of the constraint
that a tag occur exactly once in the previous block.

The bogosity is the fact that the scopes of the variables are not
clearly visible.  I use 'control-nest' when there's some kind of
repetitive structure where most variables are being rebound, the
others tend to be "nuisance" variables that you wish you could forget,
and so forth.

>   Lots of people tell their editors "go up N levels of
>   blocks/parens/what-have-you".  You've broken that (unless you teach
>   your editor about your macro).

Let me put it this way: 'control-nest' is useful when something the
programmer is thinking of sequentially is forced by the language to be
nested.  Think of it as "syntactic tail-recursion."  I grant that its
use requires maturity and steadiness of vision ....
>
> o Lots of coding has trained my intuition to expect code close to the
>   left margin to not depend on very much.  You've broken that.  (E.g.
>   you could have code all but flush-left, but it's nevertheless inside
>   a cond in a loop in an let.)

I _think_ you're just repeating the previous point, using "dependence"
as a synonym for "nesting."  My point is that nesting is not always
synonymous with dependence.  When it isn't, 'control-nest' provides you
with an alternative signaling system.
>
> I dunno.  Enough bugs me about your macro that I don't think I'd want
> to try it to see if I'm wrong.

Well, if you get a spare minute ....

> That said, by all means, let it flourish.  But if you come back to
> code a year later and have to macroexpand it to figure out wtf it's
> doing, and abandon it, please report back.

I will!

                                             -- Drew
From: Don Geddis
Subject: Re: Crazy macro
Date: 
Message-ID: <87zmht1yh7.fsf@yoda.geddis.org>
"Drew McDermott" <········@bluebottle.com> wrote on 6 May 2006 06:30:
> I have an essay on why on balance literate programming is not a very good
> idea which I may polish off and publish at some point.

Care to share any hints?  What are some of the bullet points in the
Cliff's Notes version?
_______________________________________________________________________________
Don Geddis                  http://don.geddis.org/               ···@geddis.org
The Great Roe is a mythological beast with the head of a lion and the body of a
lion, though not the same lion.  -- Woody Allen
From: Drew McDermott
Subject: Contra Literate Programming (Was: Crazy macro)
Date: 
Message-ID: <1147151175.006060.3450@j33g2000cwa.googlegroups.com>
Don Geddis wrote:
>
> Care to share any hints?  What are some of the bullet points in the
> Cliff's Notes version?

We've gone off topic completely, so don't follow up here; but I went
ahead and wrote up my plaint and put it on my blog --
http://airfoyle.blogspot.com/2006/05/anti-literacy-program.html .

   -- Drew McDermott