And / Or Macros for LISP

In case LISP programmers would like to implement && and || operators, the way they exist in C, the way to do that is as follows:

 


(defmacro && (head &rest tail)
    `(if ,head
        ,(if (null tail)
            T
            `(&& ,@tail) ) ) )

(defmacro || (head &rest tail)
    `(if ,head
        T
        ,(if (null tail)
            NIL
            `(|| ,@tail) ) ) )


 

Bear In Mind, within LISP, Not every successful function-call returns non-NIL. Therefore, I would use a slightly fancier approach, if any…

(Edit 10/02/2016 : )

 


(intern "IGN" :KEYWORD)
(intern "ign" :keyword)

(defun eval-list (input)
    (cond
        ((null input) NIL)
        ((null (listp input))
            (list (eval input)) )
        ((null (listp (car input)))
            (list (eval input)) )
        (T (cons (eval (car input))
            (eval-list (cdr input)) ) ) ) )

(defmacro && (&optional (head T) &rest tail)
    (cond ((null (listp tail))
            (error "Bad CONS Object") )
        ((eq head :IGN)
            `(progn (eval-list ',(car tail))
                (&& ,@(cdr tail)) ) )
        (T `(if ,head
                ,(if (null tail)
                    T
                    `(&& ,@tail) ) ) ) ) )

(defmacro || (&optional (head NIL) &rest tail)
    (cond ((null (listp tail))
            (error "Bad CONS Object") )
        ((eq head :IGN)
            `(progn (eval-list ',(car tail))
                (|| ,@(cdr tail)) ) )
        (T `(if ,head
                T
                ,(if (null tail)
                    NIL
                    `(|| ,@tail) ) ) ) ) )

 

(Update 7/14/2020, 23h55: )

I needed to edit both examples, due to just having learned today, that the construction of the list by the macro, and the evaluation of that list, do not both occur at runtime.


 

(Update 7/15/2020, 10h30: )

The reader might be somewhat puzzled, as to why the more-complicated version of these macros is being offered, that recognize the ‘:IGN’ keyword. The way this feature was meant to be used, for example, is in the following form:

 


(&& a b :IGN c d)

 

Assuming for the moment that my macro ‘works’, what the usage-case above will have as consequence is, that the chaining of evaluations – aka of execution – remains intact, up to the point where ‘c’ is only evaluated, if both ‘a’ and ‘b’ evaluate to True – aka to non-Nil. However, whatever list ‘c’ returns is discarded, so that the evaluation of ‘d’ is also automatic, if the situation was reached, in which ‘c’ was to be evaluated. And then finally, if and only if ‘d’ was also True, the macro will return T.

The same semantics should also work for the ‘||’ macro. However, in practice, this might be less useful, because it would mean that ‘c’ is only evaluated if both ‘a’ and ‘b’ are Nil, because ‘||’ is still searching for the first element that is True. And then, if the evaluation of ‘c’ does return non-Nil, the value returned by the corresponding macro will still be Nil, if and only if ‘d’, evaluated, returned Nil.

With the ‘&&’ macro, this keyword could lead to expressions the main purpose of which is the conditional evaluation of ‘c’.

Also, ‘c’ could be any of Nil, an Atom, a single List that begins with an Atom, or a List of Lists. Whichever ‘c’ is, the way I defined ‘eval-list’ will return a list of values, which are individually Lists or Atoms, just depending on what was evaluated each time. This might be useful elsewhere, but the ‘&&’ and ‘||’ macros discard what ‘eval-list’ returned.


 

 

If the usage scenario shown above was changed, so that the list no longer contains the argument ‘d’, what either macro will return, is the logical value of ‘a’ combined with ‘b’. In the case of the ‘||’ macro, this will be Nil, if ‘a’ and ‘b’ were Nil, even if ‘c’ returned a non-trivial value indicating some sort of success.


 

Further, the reader may find that the argument ‘c’ shown above, could be set to the following list, and that doing so will ‘work by accident':

 


((fun1 arg1) (fun2 arg2) fun3 arg3)

 

That will produce the same result, as if ‘c’ had (preferably) been set to:

 


((fun1 arg1) (fun2 arg2) (fun3 arg3))

 


 

(Update 7/15/2020, 12h45: )

The idea has occurred to me, that the first version, of the more-complex version, of my macro, might have been vulnerable to a certain type of (presumably accidental) abuse by a programmer. It could happen that, at some level of recursion, the rest parameter is atomic. This can happen because some sort of use is given, the parameter-list of which ends in a CONS object.

Normally, I’d expect that whatever code within the LISP compiler parses parameter lists, would throw a fatal error, if such a macro was included in a function definition. But a special case could exist, when there are no required parameters. The optional parameter could receive its default value, and a (re)use of the macro like so:

 


(&& . atom)
(|| . atom)

 

Could simply take place forever, or, until the user force-quits the compiler.

Because of such a possibility, I’ve needed to edit the code again.


 

(Update 7/15/2020, 14h40: )

Through sheer contemplation I’ve realized, that both versions of my code above contained a redundant re-evaluation. Edited again.

Dirk

 

Print Friendly, PDF & Email

One thought on “And / Or Macros for LISP”

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>