Formlets and LoopSun Mar 6, 2011
Just a quick update today.
First, I've pushed an update to the formlets project on github. It now supports input
type=file1. Check the [project page](https://github.com/Inaimathi/formlets), or the new wiki, both of which have slightly more robust documentation than you'll find here.
There's really no occasion to this, by the way. I try to be a self-centered realist in terms of design philosophy, so the only reason I added file fields here was that I finally found I needed them. It actually surprises me quite a bit that I got by for so long with only
recaptchas, but there you have it. I'm in the middle of another project at work now though, so I may soon add
Don't hold your breath though.
Second, I've been figuring out
loop for the past little while2. The pieces that
loop helped in are ones that would otherwise have to be expressed in terms of recursion and some intermediary variables. If you want to take a look at it in action, check out the "Validation related functions" section in [this diff](https://github.com/Inaimathi/formlets/commit/0e9f9bd1f608ff9f02867e895385c5455ce365ee#formlets.lisp). Seven lines of
loop saved me something like 12 lines of recursion and six lines of helper function. And not only that, but it's (in my opinion, obviously) much easier for a human reader to parse this way. I haven't learned it yet, and doubt I ever will, given its specification. It looks like
loop itself is, without exaggeration, several times more complicated than the rest of Common Lisp combined3.
iterate doesn't seem to be much better in this regard, by the way. It seems to be
loop with a few extra parens thrown in. While that does help with modularity,
loop isn't hard to learn because it doesn't have enough parentheses, it's hard to understand becuase it's complicated.
In any case, I've found tutorials on both iterate and loop, as well as the CL cookbook loop entry and Seibels' treatment in PCL. The two things that I needed to know in order to make that formlets code work were either omitted or buried, or merely implied. Specifically, I needed to interate by two elements of a list, and I needed to be able to return a flat list that had double the elements of the input (collecting two elements per input element). Basically
'(:a 1 :b 2 :c 3 :d 4) => '(:a :b :c :d) '(:a :b :c :d) => '(:a "A" :b "B" :c "C" :d "D")
That's very slightly beyond
mapcar as far as I know, so the way it ended up getting written was a recursion. That ended up being very complicated4. So, for my own future reference (and hopefully, for the benefit of anyone else that does a search on this), here's how you do it with loop.
;; Iterating by multiple elements (defvar test-list '(:a 1 :b 2 :c 3 :d 4)) > TEST-LIST (loop for (key value) on test-list by #'cddr collecting key) > (:A :B :C :D) ;; Collecting multiple elements (setf test-list '(:a :b :c :d)) > (:A :B :C :D) (loop for key in test-list collecting key collecting (string key)) > (:A "A" :B "B" :C "C" :D "D")
You can actually skip the "
ing" in this case, writing the last two clauses as
collect key collect (string key). There's also no requirement for making this a multi-line statement, I just feel that it's easier to read here.
Third, and no one other than me cares, so you may want to go read something interesting instead.
I know it's not very impressive yet, but keep in mind that I started off in the 35-45 range.
Hopefully, I can crack 100 this year.
- Which includes managing the enctype properly, displaying file inputs and providing a couple of basic predicate generators for validation.↩
- And re-wrote a lot of the formlet internals with it, now that I've realized that it can basically do everything.↩
- With the obvious exception of
- Not that the situation helped; this would have been relatively straightforward in regular code, but throw in two or three steps of macroexpansion, and it gets ugly fast.↩