From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: akater Newsgroups: gmane.emacs.devel Subject: [PATCH] Some improvements for cl-flet Date: Thu, 23 Sep 2021 22:37:33 +0000 Message-ID: <87mto2gbpu.fsf@gmail.com> References: <87bl4zqnqn.fsf@gmail.com> Mime-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha512; protocol="application/pgp-signature" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="13600"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Stefan Monnier To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Fri Sep 24 00:50:00 2021 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 1mTXXU-0003KI-0P for ged-emacs-devel@m.gmane-mx.org; Fri, 24 Sep 2021 00:50:00 +0200 Original-Received: from localhost ([::1]:57182 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mTXXR-0004Da-TK for ged-emacs-devel@m.gmane-mx.org; Thu, 23 Sep 2021 18:49:57 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:58600) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mTXWg-0003U9-7I for emacs-devel@gnu.org; Thu, 23 Sep 2021 18:49:10 -0400 Original-Received: from mail-ed1-x52f.google.com ([2a00:1450:4864:20::52f]:44014) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1mTXWd-0002yl-6h for emacs-devel@gnu.org; Thu, 23 Sep 2021 18:49:08 -0400 Original-Received: by mail-ed1-x52f.google.com with SMTP id v10so24130561edj.10 for ; Thu, 23 Sep 2021 15:49:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:in-reply-to:references:date:message-id :mime-version; bh=OPLqY3aXjRFnu+hlBi76wCLt/GyKDW2bSe90NO8LtpY=; b=YdUyjiqhBPV9QxHWie4VfzY/od2jx4FYmKvSpxEFd1oJIe5nyixFfG6PsfArzWExkc 4QRnTqkIWXhvmKIG2pYvRYksQonBFrePWmF/wejcPYZd/R6+0zt6eCcjHvKbxGvr57Tx NBGfN+9MfLGXbYs2z2K3uz1UquprCxA/inoJiREvMJn3FWWPwaXoXnNrmd3NkCqL1TxN PsOcLZmc4TfiBY9+30nk9zfI/1kqzepcdH+s00gqfl4z8i5v8mRqmmCnPl6YyTeMfK0d Z+nhHrUHXaPkyvilV7fIEcR4Aa0c5BpILNhRvfrWU8bbqFfEp3L0evSMczCz/M/KgwcX 83aQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:in-reply-to:references:date :message-id:mime-version; bh=OPLqY3aXjRFnu+hlBi76wCLt/GyKDW2bSe90NO8LtpY=; b=sztbsIgIyeVrqgIjxd/4UHzif/Gn2GfP55cNzs4RyX28OvVx9mdlhpZjVMewd8htdg BSoUeG/iozVJirqOuYc1OZFOCDzXqBg2BcZjnUr0J2kfruqqiMUoAvLwtVXUoIiHkaOF /IKxSvGLc87JbUEmswuuYjMcGf4uVA1zICdp+NdDwb3PHF9Md1lrfEWpiD3BLGudg4eQ 0SP6/hPJ+FP88dp+o7pOc47dKbrKTXFjbE4wfb44Q2tYATQ7FrGo/f7vi5UNFd0H0ZBf /Qy//c3JwUjzWBO3vo6cPRiHQrf0zFHUOvTyIhhAm7n95+mdKwdx+cnajx4uZZwvACyI IYaw== X-Gm-Message-State: AOAM531Y1MQ1+wS+5DPh2LxCAx8WQPucwro7pqIUypudeuHnV8M9fblx hMMAA3eZ4MwrdNjSHc5r6kKC8sKyAGdJtob9 X-Google-Smtp-Source: ABdhPJwrBTxZWa9317M8uXovzhvnTY+65+BbLgQD1Q13ygX0C7vgm1wVdAWzkkP4Ev0ZPGfvmg8lmQ== X-Received: by 2002:a17:906:a24c:: with SMTP id bi12mr7626340ejb.530.1632437344896; Thu, 23 Sep 2021 15:49:04 -0700 (PDT) Original-Received: from localhost (zrh-exit.privateinternetaccess.com. [195.206.105.217]) by smtp.googlemail.com with ESMTPSA id bc4sm4432898edb.18.2021.09.23.15.49.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Sep 2021 15:49:04 -0700 (PDT) In-Reply-To: <87bl4zqnqn.fsf@gmail.com> Received-SPF: pass client-ip=2a00:1450:4864:20::52f; envelope-from=nuclearspace@gmail.com; helo=mail-ed1-x52f.google.com X-Spam_score_int: -7 X-Spam_score: -0.8 X-Spam_bar: / X-Spam_report: (-0.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_BL_SPAMCOP_NET=1.347, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.23 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" Xref: news.gmane.io gmane.emacs.devel:275371 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Sorry, it took longer than I expected, partially due to my desire to =E2=80= =9Cdo everything right=E2=80=9D the first time. * Issues with cl-flet There are several issues with ~cl-flet~. ** No error on illegal function names #+begin_src emacs-lisp :tangle no :results code :wrap example emacs-lisp (macroexpand-1 `(cl-flet ((nil () t)) (funcall nil))) #+end_src #+RESULTS: #+begin_example emacs-lisp (let* ((--cl-nil-- (cl-function (lambda nil t)))) (progn (funcall nil))) #+end_example #+begin_src emacs-lisp :tangle no :results code :wrap example emacs-lisp (macroexpand-1 `(cl-flet (((f) () t)) (funcall (f)))) #+end_src #+RESULTS: #+begin_example emacs-lisp (let* ((--cl-\(f\)-- (cl-function (lambda nil t)))) (progn (funcall (f)))) #+end_example ** No error on malformed specs #+begin_src emacs-lisp :tangle no :results code :wrap example emacs-lisp (macroexpand-1 `(cl-flet ((f arglist yet-more)))) #+end_src #+RESULTS: #+begin_example emacs-lisp (let* ((--cl-f-- (cl-function (lambda arglist yet-more)))) (progn)) #+end_example #+begin_src emacs-lisp :tangle no :results code :wrap example emacs-lisp (macroexpand-1 `(cl-flet ((f)))) #+end_src #+RESULTS: #+begin_example emacs-lisp (let* ((--cl-f-- (cl-function (lambda)))) (progn)) #+end_example ** Incorrectly treated (setf ..) local functions This will attempt to use a (non-existent, in this case) global function instead of the generated local one: #+begin_src emacs-lisp :tangle no :results code :wrap example emacs-lisp (macroexpand-1 `(cl-flet (((setf kar) (new cons) (setf (car cons) new))) (let ((cons (cons nil nil))) (setf (kar cons) t) cons))) #+end_src It would be nice to have a support for local setf functions, as in Common Lisp. ** No warning on duplicated definitions #+begin_src emacs-lisp :tangle no :results raw :wrap quote (macroexpand-1 `(cl-flet ((f (x) (1+ x)) (f (x) x)) (f 42))) (or (get-buffer "*Warnings*") "No warnings") #+end_src #+RESULTS: #+begin_quote No warnings #+end_quote ** No warning on unused definitions #+begin_src emacs-lisp :tangle no :results raw :wrap quote (macroexpand-1 `(cl-flet ((f (x) x)) nil)) (or (get-buffer "*Warnings*") "No warnings") #+end_src #+RESULTS: #+begin_quote No warnings #+end_quote Unused definitions better be simply removed from the macroexpanded code. ** No way to capture definitions present in the body This is relevant for fixing an issue with ~cl--generic-lambda~: it needs to record the used definitions; right now it parses with an expensive and unreliable procedure. The issue was discussed with Stefan Monnier at https://lists.gnu.org/archive/html/emacs-devel/2021-09/msg00092.html and we agreed it's best to alter ~cl-flet~ to record the actually encountered definitions during macroexpansion. * Patch overview The attached patch fixes all of the above. Note: Supporting local (setf ..) functions requires significantly more code. However, I think it's worth it and I hope it gets accepted. Issue: I tried to alias ~cl--generic-with-memoization~ to ~cl--with-memoization~ but that failed at build time. For a working prototype, I just duplicate the definition. The macro itself is exactly the same. Re: capture definitions present in the body, I decided that simply collecting the actually encountered definitions during macroexpansion and returning the list would be too ad-hoc so I implemented expander that could evaluate arbitrary user-provided code during macroexpansion. Said code should be provided as 0-argument lambdas; they are only executed once; the return value is then memoized and reused during macroexpansion. The table of memoized values is used to report the =E2=80=9Cmissing=E2=80=9D and =E2=80=9Cduplicate=E2=80=9D war= nings. In a nutshell, #+begin_src emacs-lisp :tangle no :results code :wrap example emacs-lisp (macroexpand-1 `(cl-flet ((f (x y) do f stuff) (g expr)) body)) #+end_src now amounts to #+begin_src emacs-lisp :tangle no :results code :wrap example emacs-lisp (cl--expand-flet macroexpand-all-environment body 'g (lambda () (list nil expr)) 'f (lambda () (list (make-symbol (format "--cl-%s--" 'f)) `(cl-function (lambda (x y) do f stuff))))) #+end_src The first element of the returned list indicates whether the second element should be bound to a local variable, or substituted as is. There is no lighter alternative as type of expr may vary significantly, particularly due to keeping the (func exp) syntax for c~l-flet~. We could use cons rather than list for values but this way, return values can be employed as let bindings as is. It might be worth it to make lambdas accept arguments passed to local functions in the body of ~cl-flet~ and make them run each time. I found this interface and the corresponding implementation too cumbersome and not worth it in the absence of specific applications. One possible such application might be found in FIXME entry from =3Dcl-generic.el=3D: #+begin_example emacs-lisp ;; FIXME: Optimize the case where call-next-method is ;; only called with explicit arguments. #+end_example but I'm not sure I'm correct. The new implementation of ~cl-flet~ can be macroexpanded with ~macroexpand-1~ just like the old one, so everything is compatible at this level as well: #+begin_src emacs-lisp :tangle no :results code :wrap example emacs-lisp (cl-values (macroexpand-1 `(cl--flet/new ((f (x y) (+ x y))) (f 1 2))) (macroexpand-1 `(cl--flet/old ((f (x y) (+ x y))) (f 1 2)))) #+end_src #+RESULTS: #+begin_example emacs-lisp ((let* ((--cl-f-- (cl-function (lambda (x y) (+ x y))))) (funcall --cl-f-- 1 2)) (let* ((--cl-f-- (cl-function (lambda (x y) (+ x y))))) (progn (funcall --cl-f-- 1 2)))) #+end_example See the links below for =E2=89=8850 examples and tests in Org markup, for general feel of what the patch does and what I've checked. * External Links =2D [[https://framagit.org/akater/elisp-cl-flet-improvement/-/raw/master/cl= -flet-improvement.org?inline=3Dtrue][for EWW and org-mode (recommended; see= Tests section)]] =2D [[https://framagit.org/akater/elisp-cl-flet-improvement/-/blob/master/c= l-flet-improvement.org#L295][for other browsers]] --=-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQJLBAEBCgA1FiEEgu5SJRdnQOF34djNsr6xYbHsf0QFAmFNAbAXHG51Y2xlYXJz cGFjZUBnbWFpbC5jb20ACgkQsr6xYbHsf0QIUA//XTAEg9sgRlrlIHNNpn6fsBbZ //Rl4eKjxvj8oi6HmYo7ZyPcaE7igFI5pwIcNHWIAaHzn8AGx2R2rCmeAhUjq97J ex10HeXqpa45Uc5r36mwkhDX9h2sWIZe4BIPw3zEgleLq4duZ2xUGyedJ9MhbqYl /2ADH0xlV8HYRPdf+sqTk9bLUKatghJ0OYQ8xpTjWNdenGow0PKQscUQP4ZGLvxc nS1HbNNzreb3MriHCvws9rD20Z0fZbF4thXmKGPUNCoqGuspGBQBP0LBYS+s+4y1 Ip6UVe48KfHO1wwMmMqJfJtgREkSAJ/grd3Uwtlyhbp3UsaBo6iLBT7lq9XQcjwR LNJOE1zzF8eyC9aIjayiC2RHNkmmifzHeXvAUH8AM8u1j+wW+S6ktDe6n0nGdJ5O /TFwBACdBwcmr5hng7EILyCx7JLZm9Vk6Y28rxF7EIs/7f2If5weTjIEaNksGnEK WhaTC0aacKrxAdHW6IWKmW2xlSflpLFqFEJZukMjOal0LHMq9h4+MaLKovhPb8nA 9nJlHqgqne3aG3bnPPN4s8yTxB18L9iMkI5AopGGpP1cKw8aokcOVxEPfoh7y+Uy BPSlAVfukjiHwu9NHbSzi5BMLVS8Y73a7NqylRJOYtA81FQheKFFDrIu7+P1l4qW 7EtzMKtA89HThBDFy6k= =3lZm -----END PGP SIGNATURE----- --=-=-=--