Monday, June 18, 2012

Land of Lisp Chp 4

Chapter 4 covers exclusively boolean operations. It starts with a discussion of the only false data value in Lisp which is the empty set '(). This differs from Scheme, though the details of that aren't discussed as they're clearly outside the scope of the book. Note that it is represented in data mode because it is a data value, not a form.

To complicate matters (which seems to happen a lot in this chapter) '() isn't the only way to represent falsehood. This is a careful distinction which I'll come back to. The following are all false in Lisp

The first is just the empty list. The second resolves to an empty list due to the way Lisp parses forms. nil is actually a constant that resolves to 'nil and part of the Common Lisp spec is that 'nil and '() are equivalent. Why are all 4 deliberately set up this way? The book doesn't say. I suspect backwards or cross-dialect compatibility, but to be honest I don't really care right now. It is what it is, as my boss is fond of saying. The important thing is that an empty list is false, and any list that is not empty is true by definition (though in practice the 'nil can present some gotchas that will come up later). This lends Lisp to easy recursive processing of lists like so.

The above recursively cdrs to the end of the list and adds 1 for each symbol; the false branch of the if form returns 0 for the empty list at the end

Next up is if. Which has already been used but not discussed. It's hard to talk about boolean operations without 'if' in seemingly any language. Lisp's if is a bit stunted compared to something like a dialect of C. if only does one thing. Because of the way the command is structured

you can only evaluate one list. Which is a tad misleading since that one list could be a function form that does a million things or, as the book suggests, a call to progn which is glossed over here but apparently behaves similarly to an eval function. Still, it's an important thing to remember and it would tend to reinforce functional programming if that's your thing.

A key feature of if is that, like many other languages, it performs short circuit evaluation. The book spends a lot of ink on this, but I shan't bother except to say that if can do this because it is a special form as opposed to other forms which evaluate all their parameters. This is glossed over here as well as macros which are apparently similar. This will come up again in chapter 16 it seems.

There are a number of alternatives to if. when and unless are mirror siblings.

when executes its commands if {boolean} is true and, as might be guessed, unless executes its commands if {boolean} is false. These each will take an command list of arbitrary length. Both return nil and do nothing if {boolean} is not the correct value.

Finally there are 2 conditional functions that are fully flexible. I almost said functional but while the prior conditionals have been somewhat less flexible than the conditionals of other languages it's worth noting that they're easy to understand and read, and are largely self documenting. These two, on the other hand, are more like another language's switch/case statement, are complex to read (lots of syntax which, in Lisp, is parentheses and hard for an unpracticed human eye to track) and not very self-obvious. They can be formatted to be easier to read and written to be, or the surrounding code architected to be, easier to grok but there's a limit to how much that can accomplish. The book mentions none of that and perhaps I'm just suffering from a neophyte's inadequacies; it is mentioned that seasoned Lispers prefer cond because of it's generality. I don't know, but without further ado however here is the cond and case functions.

Clearly these two can quickly become hard to read. Nevertheless they're probably called for in some circumstances. cond takes a what is basically a list of when forms. The first matching {boolean} is executed and the remainder ignored. As one might expect the final {boolean} is often a true value to drive default behavior. case is nearly a C style switch statement. It takes a {symbol} which it will compare to the various {value}s defined in its body using (specifically) the eq function (this will have more meaning in a minute) and executes only the matching branch. It isn't stated explicitly but the otherwise branch is default behavior.

There are 2 boolean operator functions of note. and ... and... or. These each take a list of parameters and return a boolean based on the boolean evaluation of those parameters much like you would expect. There is an interesting side effect of the short circuit operation of these functions. Take the following example (ripped straight from the book).

In the above form the 3 parameters are fed to and. You could imagine this being called when an editing program is closed. First the *file-modified* variable is tested and if it's false and immediately returns nil. If it's true then the function (ask-user-about-saving) is tested. Presumably this takes input from the user. If the user inputs a value that is evaluated to true then the (save-file) function is executed. In theory this would return a success/failure variable which would then determine whether and returns nil or true. All of that in one little line. That's some pretty dense semantics. It might be better to use a more verbose if structure if the code needs to be maintained by neophytes since all of the relies on some subtle effects. Personally, I like the density and this case isn't terribly difficult to figure out. Maybe it's just because I grok short circuit evaluation, I dunno.

In a slight tangent the book interjects that one subtlety of the Lisp definition of false is that functions get a freebie return value. Anywhere that a function could return truth or falsehood it would be better to find some more useful value to return than just true; if you're testing whether a variable is defined you might as well return the value (unless it's nil!) since it will be evaluated as true regardless of the actual value. The book uses the member and find-if functions to discuss this but I'm not going to get into them since this is already long and I want to move on to chapter 5.

Finally we come to the equality functions. The book discusses 6 commonly used equality functions. Yes, 6! equal, eql, eq, =, string-equal, and equalp. The rule of thumb is to use eq to compare symbols (it's fast, efficient, and doesn't work well on things that aren't symbols) and to use equal for everything else. I don't have the gumption to dig far into equality comparison right now but will point out that equalp will handle case insensitive string comparisons as well as comparing integers to floating point values.

No comments: