[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Questions about (function eq)

    Date: Wed, 1 Jul 87 14:25 EDT
    From: Michael Greenberg <GREENBERG%cs.umass.edu@relay.cs.net>

    What are the semantic differences?  Is there
    a difference between

	(defun xyz ()
	    (flet ((eq (a b) (bar a b)))
	      (pushnew 'foo bar :test #'eq)))


	(defun xyz ()
	    (flet ((eq (a b) (bar a b)))
	      (pushnew 'foo bar :test 'eq)))

Yes there is.  In the first case, PUSHNEW will call your local function
EQ, while in the second case it will call the global one.  The second
one is equivalent to

	(defun xyz ()
	    (flet ((eq (a b) (bar a b)))
	      (pushnew 'foo bar :test (symbol-function 'eq))))

FLET creates a lexical binding, it doesn't do anything to the symbol's
function cell; therefore, if PUSHNEW is given the symbol, all it can do
is call SYMBOL-FUNCTION to extract its global function binding.

    What are the stylistic differences?

Since there are semantic differences, I don't think the stylistic
differences matter.  Style choices don't come into play unless you're
choosing between equivalent operations.

    Which of the following macro definitions
    is preferred?  Why?

    (defmacro foo (fn)
      (if (eq fn 'eq)
	  '(expansion-1 ...)
	  '(expansion-2 ...)))

    (defmacro foo (fn)
      (if (eq fn #'eq)
	  '(expansion-1 ...)
	  '(expansion-2 ...)))

    (defmacro foo (fn)
      (if (or (eq fn #'eq) (eq fn 'eq))
	  '(expansion-1 ...)
	  '(expansion-2 ...)))

I don't think any of them are even right.  You presumably meant the
conditional to be one of
	(equal fn ''eq)
	(equal fn '#'eq)
	(or (equal fn '#'eq) (equal fn ''eq))

because macros are passed the data structure from the form, not the
evaluation (you have to use EQUAL because they are lists).  I'm assuming
that FOO is intended to be called as
	(FOO 'EQ)
	(FOO #'EQ)

If you want to allow both forms, then my third conditional would be the
one to use.

If you want it to be callable as (FOO EQ), then your (not my) first form
would be correct.

I don't think this will do the right thing in the face of
locally-shadowed functions, though.  The conditional is only looking at
the list structure, i.e. it is checking whether its argument is the list
whose first element is either the symbol FUNCTION or the symbol QUOTE
and whose second element is the symbol EQ.  In the #'EQ case, the macro
has no way of evaluating this in the appropriate lexical environment in
order to check whether it refers to the "real" EQ function.  If the
expansion contains ,FN or #'EQ, though, it will refer to a local EQ
function if there is one, because macro expansions get processed in the
caller's lexical scope.

For this reason, I recommend that you not try to do this as a macro.  If
you do it as a function you can compare the argument against
(SYMBOL-FUNCTION 'EQ), and conditionalize it at that time.  If you
really need the performance gain that the macro provides, you should
clearly document the fact that it assumes that #'EQ is the standard EQ.

In general, macros and FLET can have some weird interactions.  Every
macro makes assumptions about the functional values of symbols.  For

(defmacro bar (a b)
  `(list (cons ,a 1) (cons ,b 2)))

makes an assumption about LIST.  If one then did

(defun quux (c)
  (flet ((list (thing) (car thing)))
    (bar c (1+ c))))

an error would be reported because the local function LIST was being
called with the wrong number of arguments, because this is equivalent to

(defun quux (c)
  (flet ((list (thing) (car thing)))
    (list (cons c '1) (cons (1+ c) '2))))

Macros, as they are currently defined, don't obey lexical scoping,
because they are very simple rewrite rules.

There has been some research on new ways to define macro expansion that
don't have these problems, and there is some investigation taking place
in X3J13, the ANSI Common Lisp standardization committee.  For now,
though, that's the way it is.