Idiomdrottning’s homepage

Descend

More three page Scheme macros to shave off one parenthesis by implementing a bunch of complex, conditional, implicit, magic, DWIMmy conventions? Of course!

Folds are great and all but they don’t let you easily exit early without explicitly calling a stored continuation. So sometimes a named let is just way easier. Except so wordy.

Descend is sort of like a named let except it does three magic things.

First of all, the let tag is always desc. I almost named the macro itself desc but that would’ve been bad since then you couldn’t nest them.

Second of all, if the binding is to a value of the same name e.g. (lis lis) you can just put the name there. You can mix these shorthand bindings with normal bindings.

Now, if the first binding starts out as empty, the third magic thing (which I’ll get into shortly) is disabled and you can go on your merry way only using the above two magics.

(descend ((sum 0) (nums '(1 2 3 4)))
         (if (null? nums) sum
             (desc (+ sum (car nums)) (cdr nums))))

⇒ 10

If it starts out non-empty…

(descend ((nums '(1 2 3 4)))
         (+ (car nums) (desc (cdr nums))))

⇒ 10

That’s right. It only recurs if the value is non-empty.

Here is another example of descend used in action, to define an “not implemented yet” operator:

(define (niy . test)
  (if
   (null? test)
   (error "Not implemented yet!")
   (descend (test)
            (if (car test)
                (error  "Unimplemented value case!")
                (desc (cdr test))))))

This errors if called with no arguments or if any of its arguments are true.

So you can stick (niy) in a weird corner case cond clause or you can put

(niy (null? server-response))

or whatever as a guard clause. For when you are hacking and you want to get something messy up and running quick. Sort of like a living FIXME that actually errors out.

Specific semantics of the third magic

Only the first (leftmost) descend binding is magic.

Consing

(cons (desc foo ...) bar) or (cons bar (desc foo ...))

gets wrapped in an (if (empty? foo) (list bar) ...) form.

Unary

(bar (desc foo ...))

gets wrapped in an (unless (empty? foo) ...) form.

Binary (other than consing)

(baz (desc foo... ) bar) or (baz bar (desc foo ...))

gets wrapped in an (if (empty? foo) bar ...) form.

Ternary or more

(q a b c (desc foo ...) d e f)

gets wrapped in an (if (empty foo) (q a b c d e f) ...) form.

Source code

descend and niy are now part of brev-separate.

git clone https://idiomdrottning.org/brev-separate