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

Reply via email to