I don't see any change in behavior. I thought maybe you were trying to use lst in two different senses and wanted to be clear about that, but it's not that either..
In wart all args are optional (like javascript), so I could just:
(mac aif(expr then . more-branches)
(if (or then more-branches) ; <= modified
`(let it ,expr
(if it
,then
(aif ,@more-branches)))
expr))
It's a utility to cut down on nesting (iflet (a . b) ...) forms, a common pattern I use when giving a utiility different behavior depending on the number of arguments. The primary goal is to have the branches lined up on the right side while the local variables are distributed so they don't get in the way. Particularly, a variable is positioned just before the first branch it's bound in. The rest variable is an exception; it makes sense at every level, but I don't want it to be perceived as the first element of the list, so I've put it in a different place. (Note that it doesn't actually make sense everywhere unless the thing being destructured could be a dotted list or non-nil atom.)
The three branches in my 'aif's use of 'delisting are the zero-element branch (just nil), the one-element branch, and the two-or-more-element branch. The "two or more" thing might be tricky to perceive; maybe the utility should be limited to proper lists and the last case should be preceded by both its usual variable and the rest variable.
Meanwhile, the point of 'g-rest is to allow for destructuring. Destructuring an atom might be weird, but most of the time the rest isn't an atom, and nil can be destructured just fine.
Ok, I'm starting to see. So the code in each branch binds all variables upto that point?
When I see:
first first
I'm inclined to read this as, "if you just see a variable (call it first) and nothing else after, just return first." I imagine the evaluation going to the next case and saying[1], "oops, nothing left, ok, go back and execute code for the previous case." Am I on the right track? Hmm, I've never seen anything like this.
And the nil is a branch with an empty 'guard'. Ahhhh.. this is cool :)
Ack, I misunderstood you. I've retreated to a version with car/cdr manipulation; looks like there's no way around it (it's still simpler than the original, but that just makes me wonder what bugs remain)
I haven't tested it, but I don't see any bugs from here.
I'm a bit surprised you broke up the (if branches ...) condition into two places like that, though. I think it would have been simpler just to replace "(or then more-branches)" with "branches". ^^
I prefer having 'aif be a special case of 'iflet too. The change you've made would only come up every so often. It might break some code that uses 'iflet for destructuring and the tests some other condition if that fails (whose result can't be destructured), but that's easy enough to fix.
For Penknife I've been thinking about something a bit different:
[itif x
do-something-with.it
ifdecap first rest lst
[do-something-with first rest]
iflet result foo.bar
baz.result
do do-something-else.]
In this in-progress draft, every if-like construct parses its elses using the same utility. The point is to let them work together better while making the indentation less controversial and confusing than it is in Arc. The downside so far is that every single unconditional else needs a 'do in front of it to look nice, which feels a bit like (cond ((#t ...))).
Penknife's version of (iflet (a . b) x ...) is [ifdecap a b x ...]. There's no destructuring yet, not to mention that Penknife has no notion of a dotted list.
I called 'ifdecap "ifpair" at one point, but I want it to be extensible, with a clear metaphor of first and rest rather than left and right.
Can you think of a better one? XD This isn't supposed to be that low-level. I'm working on the Penknife core from a top-down view right now, and I've considered having all argument lists decompose using ifdecap just so that a function can be applied to a user-defined type of list (which might support a user-defined kind of destructuring, for instance; think keyword args).
Come to think of it, ifdecap is a bit paradoxical.... If it's used to destructure argument lists, then it'll be recursive upon itself over and over and probably never get anywhere. (It needs to return two values somehow, which means either the caller or a CPS-style callback will need to destructure those values. I don't plan to look for a multiple-value-return-esque solution.) I guess the built-in argument list type will need some kind of hardwired treatment just like the built-in function type.
:) This is a pretty good exhibition of features: https://github.com/akkartik/wart/blob/master/023conditionals.... You can see that the '? for optional' syntax hasn't changed, that $vars and o$vars are convenient and make several macros shorter. I suppose the fact of all args being optional is a little subtle and needs context to read between the lines.
But now I've let you put off trying wart again :) At some point I want to put a little webapp together to make reading wart code more convenient. So you can click 'next' from boot.lisp to go to 000.lisp (next in load sequence), tests are just a click away, etc. Really my only goal is to make it a pleasure to read, and I'd give an arm to hear experiences of lisp programmers reading it.
I haven't had any trouble with optional args (though this is the first time I've noticed them being useful). Or breaking constraints in general. I suspect the problem is not constraints I've broken but constraints I'm not even aware of.