On 04/21/2014 09:25 PM, Stefan Monnier wrote: >>> That's not the kind of example I was thinking if. Do you have a real >>> example, maybe? >> Try writing a macro that emits a defstruct, then a function that uses >> cl-typep for that struct, all wrapped in the same toplevel progn. > > That's getting closer to an actual example. I still can't think of > a case where you'd want to use cl-typep in this way, tho. cl-struct-slot-value uses typep It makes sense to do so given that cl-struct-slot-value knows the name of the struct, but not necessarily the name of its predicate function --- which might not even exist. The chunk below is from the iface code I'm working on. This code isn't even close to done --- it's just here to give you a sense of the problem. You see that we declare a structure, then emit some functions that use the structure. Some of these functions go on to do things with values that are allegedly of the structure type, and they use typep to check that these values are actually of the struct type. This approach is a perfectly logical way to go about writing code, and we should support it. Forcing users to do something else is especially pernicious because the problem we're discussing only shows up when compiling (or loading) files --- in normal interactive development, everything seems fine! (eval-and-compile (defun iface--make-member-wrapper (iface-name member) "Create a lisp form defining an interface accessor. IFACE-NAME is a symbol giving the name of the interface being defined. MEMBER is an element of the MEMBERS parameter to `iface-declare'." (cl-destructuring-bind ((member-name inst-arg &rest xarglist) &optional doc) member (unless (and inst-arg (symbolp inst-arg) (not (eql (aref (symbol-name inst-arg) 0) ?\&))) (error (concat "Interface members must accept an instance as" " their first argument"))) `(defun ,member-name (,inst-arg &rest xargs) ,doc (declare (advertised-calling-convention (,inst-arg ,@xarglist) ,emacs-version)) (apply (cl-struct-slot-value ',iface-name ',member-name ,inst-arg) ,inst-arg xargs))))) (cl-defmacro iface-declare ((name base) &rest members) "Create a new interface called NAME, inheriting from BASE. MEMBERS is a list of member specifiers. Each is a list of the form ((NAME . ARGUMENTS) DOCUMENTATION), where NAME is a symbol naming the interface member, ARGUMENTS is a cl-lib-style argument list, and DOCUMENTATION is documentation to attach to that function." (let ((doc (and (stringp (car members)) (pop members))) (prefix (concat (symbol-name name) "--iface-"))) `(progn (cl-defstruct (,name (:include ,base) (:conc-name ,(intern prefix)) (:copier nil)) ,@(if doc (list doc)) ,@(mapcar #'caar members)) ,@(mapcar (lambda (member) (iface--make-member-wrapper name member)) members) ',name))) >> The reason we have automated tests is to make sure we can maintain this >> "level of detail". That it's not immediately useful to you isn't a >> reason not to include it. I can't believe this issue is even >> contentious: the current behavior is a clear bug. > > It's a clear bug if we assume Common-Lisp semantics. But in many cases, > Elisp chooses to provide simpler semantics, to allow a simpler and/or > more naive implementation. I don't care whether we diverge from CL in sets of supported features and names of functions, but we should try hard to match CL's fundamental execution semantics. Practically every possible bad lisp idea was tried before Common Lisp came around. The CL people knew about the precise problem we're discussing on this thread and codified the best solution in the language. There are solutions in CL for problems that we haven't even encountered yet. The less closely we follow CL, the less we benefit from the experience baked into the specification and the harder it is to fix problems that eventually do come up.