From: pjb@informatimago.com (Pascal J. Bourguignon)
To: help-gnu-emacs@gnu.org
Subject: Re: Expected behavior of CL special forms, 'labels' and 'flet', inside macros.
Date: Tue, 04 Aug 2009 03:49:38 +0200 [thread overview]
Message-ID: <87r5vs8i19.fsf@galatea.local> (raw)
In-Reply-To: mailman.3797.1249343677.2239.help-gnu-emacs@gnu.org
Jon Strait <jstrait@moonloop.net> writes:
> Hello,
>
> I thought I'd run this issue by the list in case someone who's worked
> on the CL extension code can say that it's the proper behavior and not
> worth submitting a bug report on.
>
> Basically, I tried using a 'labels' form to bind a function inside a
> macro to be used recursively and it wasn't working. (Lisp nesting
> exceeds max error) Then, I switched the 'labels' with 'flet', which
> ended up working fine, but not until after having to change the
> binding name as well. Using the same name for 'flet' as was previous
> used for 'labels' resulted in this strange error:
If you have a look at the macroexpansions, you will see that flet
shadows the original function name (and therefore it's understandable
that emacs lisp has difficulties if it was a macro), while labels
makes a substitution of the occurences of the defined functions.
> "Use `labels', not `flet', to rebind macro names"
>
> Is seems for some reason, the 'labels' functions binding name was
> registered with a macro name as well.
This could be considered a bug (vs. CL), or at least a feature
request, to have flet be able to shadow macros.
> Be advised, this is only in the context of testing both the macro and
> the calling code with eval-last-sexp inside a buffer.
>
> For the sample macro I created below, the first argument is a list of
> items and every subsequent argument is a lambda function taking one
> argument, to apply in sequence to the initial argument list using
> mapcar:
>
> (defmacro map-chain (init-list &rest mfuncs)
> (setq mfuncs (reverse mfuncs))
> (flet ((fmap (init-list mfuncs)
> (if (null mfuncs)
> init-list
> `(mapcar ,(car mfuncs) ,(fmap init-list (cdr mfuncs))))))
> (fmap init-list mfuncs)))
Or use let instead of setq:
(defmacro map-chain (init-list &rest mfuncs)
(let ((mfuncs (reverse mfuncs)))
(flet ((fmap (init-list mfuncs)
(if (null mfuncs)
init-list
`(mapcar ,(car mfuncs) ,(fmap init-list (cdr mfuncs))))))
(fmap init-list mfuncs))))
If flet was implemented with the same semantics as in Common Lips,
that macro wouldn't work. Indeed, you'd better use labels.
In Common Lisp:
C/USER[319]> (macroexpand '(map-chain '(7 8 9) (lambda (x) (* x 10)) (lambda (y) (+ y 4))))
*** - EVAL: undefined function FMAP
It works in emacs lisp because flet is implemented as a dynamic
binding, and therefore when fmap is executing, fmap is fbound to the
redefinition.
In Common Lisp:
C/USER[322]> (defun example (x) (list 'toplevel x))
EXAMPLE
C/USER[323]> (flet ((example (x) (if (oddp x) (list 'lexical 'example x) (example (1- x)))))
(values (example 1)
(example 2)))
(LEXICAL EXAMPLE 1) ;
(TOPLEVEL 1)
Both calls to example (example 1) and (example 2) call the lexical
local function. With x bound to 1, the first list is returned
directly; with x bound to 2, the toplevel function is called, from the
local funciton.
In emacs lisp:
(defun example (x) (list 'toplevel x))
--> example
(flet ((example (x) (if (oddp x) (list 'lexical 'example x) (example (1- x)))))
(values (example 1)
(example 2)))
--> ((lexical example 1)
(lexical example 1))
is not what was expected from (require 'cl)... You could report a bug,
but again, if you really want Common Lisp semantics, it might be
better to use emacs-cl.
--
__Pascal Bourguignon__
next parent reply other threads:[~2009-08-04 1:49 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <mailman.3797.1249343677.2239.help-gnu-emacs@gnu.org>
2009-08-04 1:49 ` Pascal J. Bourguignon [this message]
2009-08-03 8:04 Expected behavior of CL special forms, 'labels' and 'flet', inside macros Jon Strait
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87r5vs8i19.fsf@galatea.local \
--to=pjb@informatimago.com \
--cc=help-gnu-emacs@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.