From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Stefan Monnier Newsgroups: gmane.emacs.devel Subject: Re: Distinguishing `consp` and `functionp` Date: Mon, 29 Jan 2024 10:19:50 -0500 Message-ID: References: <86msssble8.fsf@gnu.org> <86bk9448ai.fsf@gnu.org> Mime-Version: 1.0 Content-Type: text/plain Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="39326"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Cc: emacs-devel@gnu.org To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Mon Jan 29 16:21:50 2024 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1rUTSH-000A0B-2b for ged-emacs-devel@m.gmane-mx.org; Mon, 29 Jan 2024 16:21:49 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rUTQX-00028N-29; Mon, 29 Jan 2024 10:20:01 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rUTQV-00020k-7g for emacs-devel@gnu.org; Mon, 29 Jan 2024 10:19:59 -0500 Original-Received: from mailscanner.iro.umontreal.ca ([132.204.25.50]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rUTQS-0007fP-M0; Mon, 29 Jan 2024 10:19:58 -0500 Original-Received: from pmg3.iro.umontreal.ca (localhost [127.0.0.1]) by pmg3.iro.umontreal.ca (Proxmox) with ESMTP id 8DD12442430; Mon, 29 Jan 2024 10:19:53 -0500 (EST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca; s=mail; t=1706541591; bh=62h1q/DCEwRI/9aFBQCbWl8t2rj9OpuaQ6BiafHIIdE=; h=From:To:Cc:Subject:In-Reply-To:References:Date:From; b=BbaBWZyttGS9iWBOqnJuxe8kzOfP85IiKwC5nN/Vsb7HVy8bLCe4qaCHwPTW6T2+n 2hgoj7T3QSTXgNZEDeFi7L961HxbLpYBlhzg/MwRXXSiXP0lQ0KqXNWHdX3YvEMTlP IvZjHKcpDGvXCJ/9LuNt0R92tacwCOdYBbT5mgECKuPZjJ/axX3UWFadcvP9Vn+dyI zpDe7swtWXPF8Bj6y8yos5xCCbD1wek8DmZt5ZVW/RZFFMdxdK1HegekwUpNGuek7N wb06L2C+Es/J53LCT56YSI6CCHVsXvqb6yzTYw5hHdzd08ri/fDhz60bDz0+Y+R//b jQxgxq/t1OtZQ== Original-Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1]) by pmg3.iro.umontreal.ca (Proxmox) with ESMTP id A307A44242B; Mon, 29 Jan 2024 10:19:51 -0500 (EST) Original-Received: from pastel (unknown [45.72.206.68]) by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id 802AA1209F7; Mon, 29 Jan 2024 10:19:51 -0500 (EST) In-Reply-To: <86bk9448ai.fsf@gnu.org> (Eli Zaretskii's message of "Mon, 29 Jan 2024 14:45:57 +0200") Received-SPF: pass client-ip=132.204.25.50; envelope-from=monnier@iro.umontreal.ca; helo=mailscanner.iro.umontreal.ca X-Spam_score_int: -42 X-Spam_score: -4.3 X-Spam_bar: ---- X-Spam_report: (-4.3 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:315590 Archived-At: > 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