[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Distinguishing `consp` and `functionp`
|
From: |
Stefan Monnier |
|
Subject: |
Re: Distinguishing `consp` and `functionp` |
|
Date: |
Mon, 29 Jan 2024 10:19:50 -0500 |
|
User-agent: |
Gnus/5.13 (Gnus v5.13) |
> If so, what are the situations where a Lisp program in Emacs would
> like or need to "look inside a function value"?
The most obvious cases are in cases of introspection (things like `C-h
f` and underlying functions like `help-function-arglist`).
But there have been some other cases over the years.
E.g. before OClosures:
(defun kmacro-extract-lambda (mac)
"Extract kmacro from a kmacro lambda form."
(let ((mac (cond
((eq (car-safe mac) 'lambda)
(let ((e (assoc 'kmacro-exec-ring-item mac)))
(car-safe (cdr-safe (car-safe (cdr-safe e))))))
((and (functionp mac)
(equal (interactive-form mac) '(interactive "pkmacro")))
(let ((r (funcall mac 'kmacro--extract-lambda)))
(and (eq (car-safe r) 'kmacro--extract-lambda) (cdr
r)))))))
(and (consp mac)
(= (length mac) 3)
(arrayp (car mac))
mac)))
where the `(assoc 'kmacro-exec-ring-item mac)` looks inside the code of
the function. Or in Emacs-22's `vc.el`:
(defun vc-exec-after (code)
"Eval CODE when the current buffer's process is done.
If the current buffer has no process, just evaluate CODE.
Else, add CODE to the process' sentinel."
(let ((proc (get-buffer-process (current-buffer))))
(cond
;; If there's no background process, just execute the code.
;; We used to explicitly call delete-process on exited processes,
;; but this led to timing problems causing process output to be
;; lost. Terminated processes get deleted automatically
;; anyway. -- cyd
((or (null proc) (eq (process-status proc) 'exit))
(eval code))
;; If a process is running, add CODE to the sentinel
((eq (process-status proc) 'run)
(let ((sentinel (process-sentinel proc)))
(set-process-sentinel proc
`(lambda (p s)
(with-current-buffer ',(current-buffer)
(goto-char (process-mark p))
,@(append (cdr (cdr (cdr ;strip off `with-current-buffer buf
; (goto-char...)'
(car (cdr (cdr ;strip off `lambda (p s)'
sentinel))))))
(list `(vc-exec-after ',code))))))))
(t (error "Unexpected process state"))))
nil)
where the deep nesting of cars and cdrs digs into the guts of a function.
Note that neither of those two cases would be affected by my patch (not
only because they're not with us any more): they look inside functions
they built themselves and that never go through `Ffunction` nor through
the byte-compiler, so they always remain as lists starting with the
`lambda` symbol (never converted a `byte-code-function-p` or a list
starting with `closure` or a native-compiled subr).
I recently came across another instance out in the wild, in the
[Buttercup](http://elpa.nongnu.org/nongnu/buttercup.html) package:
(defun buttercup--enclosed-expr (fun)
"Given a zero-arg function FUN, return its unevaluated expression.
The function MUST be byte-compiled or have one of the following
forms:
\(closure (ENVLIST) () (quote EXPR) (buttercup--mark-stackframe) EXPANDED)
\(lambda () (quote EXPR) (buttercup--mark-stackframe) EXPR)
and the return value will be EXPR, unevaluated. The quoted EXPR
is useful if EXPR is a macro call, in which case the `quote'
ensures access to the un-expanded form."
(cl-assert (functionp fun) t "Expected FUN to be a function")
(pcase fun
;; This should be the normal case, a closure with unknown enclosed
;; variables, empty arglist and a body containing
;; * the quoted original expression
;; * the stackframe marker
;; * the macroexpanded original expression
(`(closure ,(pred listp) nil
(quote ,expr) (buttercup--mark-stackframe) ,_expanded)
expr)
;; This a when FUN has not been evaluated.
;; Why does that happen?
;; A lambda with an empty arglist and a body containing
;; * the quoted original expression
;; * the stackframe marker
;; * the expanded expression
(`(lambda nil
(quote ,expr) (buttercup--mark-stackframe) ,_expanded)
expr)
;;; This is when FUN has been byte compiled, as when the entire
;;; test file has been byte compiled. Check that it has an empty
;;; arglist, that is all that is possible at this point. The
;;; return value is byte compiled code, not the original
;;; expressions. Also what is possible at this point.
((and (pred byte-code-function-p) (guard (member (aref fun 0) '(nil
0))))
(aref fun 1))
;; Error
(_ (signal 'buttercup-enclosed-expression-error (format "Not a zero-arg
one-expression closure: %S" fun)))))
That code will/would be impacted by my patch. I already sent a patch to
the authors to help them use another solution (in this case OClosures):
https://github.com/jorgenschaefer/emacs-buttercup/issues/241
Stefan
- Re: Distinguishing `consp` and `functionp`, (continued)
- Re: Distinguishing `consp` and `functionp`, Stefan Monnier, 2024/01/26
- Re: Distinguishing `consp` and `functionp`, João Távora, 2024/01/26
- Re: Distinguishing `consp` and `functionp`, Stefan Monnier, 2024/01/26
- Re: Distinguishing `consp` and `functionp`, Daniel Mendler, 2024/01/26
- Re: Distinguishing `consp` and `functionp`, João Távora, 2024/01/27
- Re: Distinguishing `consp` and `functionp`, Po Lu, 2024/01/27
- Re: Distinguishing `consp` and `functionp`, João Távora, 2024/01/27
- Re: Distinguishing `consp` and `functionp`, Richard Stallman, 2024/01/27
Re: Distinguishing `consp` and `functionp`, Stefan Monnier, 2024/01/28
- Re: Distinguishing `consp` and `functionp`, Eli Zaretskii, 2024/01/29
- Re: Distinguishing `consp` and `functionp`,
Stefan Monnier <=
- Re: Distinguishing `consp` and `functionp`, Eli Zaretskii, 2024/01/29
- Re: Distinguishing `consp` and `functionp`, Stefan Monnier, 2024/01/29
- Re: Distinguishing `consp` and `functionp`, Eli Zaretskii, 2024/01/29
- Re: Distinguishing `consp` and `functionp`, Stefan Monnier, 2024/01/29
Re: Distinguishing `consp` and `functionp`, João Távora, 2024/01/29
Re: Distinguishing `consp` and `functionp`, Eli Zaretskii, 2024/01/29
Re: Distinguishing `consp` and `functionp`, João Távora, 2024/01/29
Re: Distinguishing `consp` and `functionp`, Eli Zaretskii, 2024/01/29
Re: Distinguishing `consp` and `functionp`, Andreas Schwab, 2024/01/29
Re: Distinguishing `consp` and `functionp`, João Távora, 2024/01/29