Hmm, interesting. If I were opting for another control structure (which I probably wouldn't in this case, but hypothetically), I think I'd make your ibona closer to this (not tested):
(mac unless-both (test x y neither)
(w/uniq (f fx fy)
`(withs (,f (testify ,test) ,fx (,f ,x) ,fy (,f ,y))
(or (and ,fx ,fy)
(and (no ,fx)
(no ,fy)
,neither)))))
(def congruent (x y)
(unless-both atom x y
(and (congruent (car x) (car y))
(congruent (cdr x) (cdr y)))))
(def congruent-sigs (x y)
(unless-both atom x y
(and (unless-both optional (car x) (car y)
(congruent-sigs (car x) (car y)))
(congruent-sigs (cdr x) (cdr y)))))
I'm not sure if there's a better name than unless-both. Also, you could conceivably make neither a rest arg and just splice it into the and so you don't have to do (unless-both atom x y (and ...)). I just think it reads better with the explicit and. (Of course, adding a macro here is probably overkill anyway. Fun, though!)
Hmm... my code originally looked a lot like that (except for naming and the implementation of 'unless-both). Then I edited my post quite a bit because I thought there was a problem with the (and (unless-both ...) (congruent-sigs ...)) expression. (I can't remember what it was now, and I think I was mistaken.)
So I added the 'either-way parameter, and then I moved the logic into a function to make the evaluation order more to my liking--no calling the test on 'x before 'y is evaluated--and it got to the complicated state it's in now. In fact, I see some bugs now; the place where it says "do.test.y (aand (ifneither)" should be "do.test.y nil (aand (do.ifneither)".
For whatever it's worth, here's 'ibona again, with your much better name, and without 'eitherway. The only thing that's really different from your version is the argument evaluation timing.
(mac unless-both (test x y ifneither)
`(fn-unless-both ,test ,x ,y (fn () ,ifneither)))
(def fn-unless-both (test x y ifneither)
(zap testify test)
(if do.test.x do.test.y
do.test.y nil
(do.ifneither)))