There's an old programmers hack that works in many languages of abusing the logical or operator to try a sequence of operations until one returns something useful. It appears that this works in Clojure, too.
The reason I tried it is that I'm unhappy with Clojure's implementation of cond. Consider the following interaction: You are in a shed. There is a box here There is a knife here => open the box You open the box. There is a knife in the box => Take the knife out of the box You take the knife out of the box Now, consider a recursive repertoire structure in which each element is a tuple {token, before-action, sub-repertoire, after-action} a subset of which is like this (("take" () (("the" (bind-target-and-continue player (cdr line) (caddr repertoire) context) (("out" () () (take-from-container player (cdr line) (caddr repertoire) context)) )) (take-from-environment player (cdr line) (caddr repertoire) context))) The action parts may internally recurse into the interpreter; so for example bind-target-and-continue puts the token that follows "the" into the context as the target and then re-enters the interpreter with the remainder of the line and the sub-repertoire So given 'take the knife out of the box' ==> at "take" there is no before action, so we interpret the sub- repertoire ====> at "the" there is a before action, so we bind "knife" as target, and interpret the rest of the line with the sub-repertoire ======> at "out" there is no before-action or sub-repertoire, so we evaluate the after-action which tells us to treat what's left of the line ("of the box") as a container specifier, locate a container that matches that description in the environment, and take the knife out of it, rather than the knife in the environment But given just 'take the knife' ==> at "take" there is no before action, so we interpret the sub- repertoire ====> at "the" there is a before action, so we bind "knife" as target, and interpret the rest of the line with the sub-repertoire <==== but there is no rest-of-line, so interpreting it returns null ==> so we interpret the post action on take, and take the knife from the environment, not the one in the box. You can build up remarkably convincing interpreters for a subset of imperative English with this and a few Eliza style tricks. In portable standard lisp one would write (cond ((eval before-action)) ((interpret player sub-repertoire context)) ((eval after-action))) This is because each clause of the cond statement was an implicit do list, so (let ((a 'foo)(b nil)) (cond (a) (b))) => foo Each of a and b are evaluated at most once (as a has a non-nil binding in this case b is not evaluated at all). To achieve this with Clojure's cond I have to do either (let [a 'foo b nil] (cond a a b b)) => foo in which case a is evaluated twice; or (let [a 'foo b nil] (let [value-of-a a value-of-b b] (cond (not (= nil value-of-a)) value-of-a (not (= nil value-of-b)) value-of-b))) => foo which only evaluates a once but it always evaluates b whether it needs to or not, /and/ it's fugly and hard to read, /and/ it's very likely inefficient (but someone more fluent in Clojure could probably replace my (not (= nil thing)) with something cleaner); or else (let [a 'foo b nil] (or a b)) => foo which is simple and easy to read, but is exploiting the left-to-right evaluation strategy of the or operator. It isn't (or ought not to need to be) part of the contract of the or operator that it evaluates left to right. On a massively parallel machine, all branches of the or might be explored simultaneously. So this sort of hack leaves me feeling dirty. Comments? Is there (once again) something more elegant I've missed?
-- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en