From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Michael Heerdegen Newsgroups: gmane.emacs.devel Subject: Re: `thunk-let'? Date: Wed, 22 Nov 2017 03:50:47 +0100 Message-ID: <87h8tnowl4.fsf@web.de> References: <87infp9z6j.fsf@web.de> <87zi90eehg.fsf@web.de> <87o9ocd6s4.fsf@web.de> <87wp2zcwm2.fsf@web.de> <87mv3vwb2c.fsf_-_@web.de> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1511319106 13876 195.159.176.226 (22 Nov 2017 02:51:46 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Wed, 22 Nov 2017 02:51:46 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (gnu/linux) Cc: Nicolas Petton , Eli Zaretskii , Emacs Development To: Stefan Monnier Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Wed Nov 22 03:51:41 2017 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1eHL8P-0002s2-7n for ged-emacs-devel@m.gmane.org; Wed, 22 Nov 2017 03:51:33 +0100 Original-Received: from localhost ([::1]:37523 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eHL8W-0004i5-FL for ged-emacs-devel@m.gmane.org; Tue, 21 Nov 2017 21:51:40 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:33348) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eHL7r-0004gm-Rz for emacs-devel@gnu.org; Tue, 21 Nov 2017 21:51:01 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eHL7o-0002Qg-Mz for emacs-devel@gnu.org; Tue, 21 Nov 2017 21:50:59 -0500 Original-Received: from mout.web.de ([212.227.15.14]:54821) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1eHL7o-0002QK-9w; Tue, 21 Nov 2017 21:50:56 -0500 Original-Received: from drachen.dragon ([88.66.201.17]) by smtp.web.de (mrweb002 [213.165.67.108]) with ESMTPSA (Nemesis) id 0Lo0M4-1eoE6K31Vg-00fxWS; Wed, 22 Nov 2017 03:50:49 +0100 In-Reply-To: (Stefan Monnier's message of "Thu, 09 Nov 2017 13:48:43 -0500") X-Provags-ID: V03:K0:qsnR6YMHm/FELvV3H7Me6LVoR+Gaa79vcFb4vv/oL1VJJ+48ZY9 hwWqv8XrWXT2wlxG/63FvfpC5Hz1TPDwi3VufzZTwh1qrxO1kyXZHihS6MjvyFv83MILMuk WkFU48UhkH4wnYZolMyaRCnBL5VWhpDYaDwb2cKYIXEQRx+lMr0LiH+ILLNLkcuAeQDIEr8 FpKBj9WMncLDGvwO36aOg== X-UI-Out-Filterresults: notjunk:1;V01:K0:d5OfFVlmmus=:PQfQnydLIZrmazrXH2m93t CSUcjpY8K9E4jwDdrmiyowAOokVOQKo4KFRCSZl8V7Z2RXHXMeprEBQWiqUsxiPd2QtBv1rLF Gqjqn4T6a26wE4Q+oh79tcVo5WvCBfz+dt6a4F6gwoiekVbat2oBLS+hB0Wos0kW33A0hPQ6W 0bB5wQsL8+unCQgC6PwleQqmNOMEus79Bs958swvSqYhFj+5hVZEAx24EpbEoKqh7swVUmwS/ Pz/N0oESqdcrXq0LXp+6x126z4p3tKiGVXKgSbpJk9frBn91c/XxJPrhKG6WdcNkKTOU+LlbF 3/d9/EadJ7g1zwrtXBjWYwmjOIhXPMFISz6/dwucUYQ3ZCTxXTpQBBoSPeWlebjP6Z9eS6UhL IIMhgcMDmvSD7ktLpuBGYD7/5AJRvhINddmqQEVRMVJwwchI2nxbezPTCPmJ/j3Yc+VAH3AXh 4mB+LDGKfEJnaNZyJz2NIPi7uUidjgUsK5PfNcSp094goigFzA7RY3d3dS3fLd3t9rfjg7LHp 8tIbaRZ1qardcUfpu4SH9eElwElM6q+gRhEvHVWVOM3SouV2sfr3DyuL6M3oEvp1fiIYCOz0w AVJHUzdEBhMb59ijn3ozSRDyVL4BA5CoUN9EZ78CzqJygHWHUJ5SOqIIbrDI3jee39moplTDw 9Uh2WdYtmgQf/3I3KOBXmaS9jaGQ8LtxmQh51T2CapVpqlrUKForCwcKvT91Iiv0MJbgG9kTC FM11+ZnJQWeL2EU2dqcdKGvShJY8YtgoFMiKaNlh4n+7x3242tFL+QbjNU7eLmB/Un9iOEc0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 212.227.15.14 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 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.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:220345 Archived-At: --=-=-= Content-Type: text/plain Stefan Monnier writes: > Yeah, pretty ugly. Now I see why my intuition told me that lazy-let > should be in thunk.el ;-) Ok, I updated the patch accordingly (attached): the stuff goes to thunk.el now, I added a note to the file header, renamed everything, added some more details to the docstrings, moved the tests, and removed the "---" from the News entry. --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Add-macros-thunk-let-and-thunk-let.patch >From 250b999e094ccdf11e37bb80f2d6a41b64b96dfd Mon Sep 17 00:00:00 2001 From: Michael Heerdegen Date: Thu, 2 Nov 2017 18:45:34 +0100 Subject: [PATCH] Add macros `thunk-let' and `thunk-let*' * lisp/emacs-lisp/thunk.el (thunk-let, thunk-let*): New macros. * test/lisp/emacs-lisp/thunk-tests.el: (thunk-let-basic-test, thunk-let*-basic-test) (thunk-let-bound-vars-cant-be-set-test) (thunk-let-laziness-test, thunk-let*-laziness-test) (thunk-let-bad-binding-test): New tests for `thunk-let' and `thunk-let*. --- etc/NEWS | 4 +++ lisp/emacs-lisp/thunk.el | 56 +++++++++++++++++++++++++++++++++++++ test/lisp/emacs-lisp/thunk-tests.el | 50 +++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) diff --git a/etc/NEWS b/etc/NEWS index c47ca42d27..bfa8be39f9 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -109,6 +109,10 @@ Snake and Pong are more playable on HiDPI displays. *** Completing filenames in the minibuffer via 'C-TAB' now uses the styles as configured by the variable 'completion-styles'. +** Thunk +*** The new macros 'thunk-let' and 'thunk-let*' are analogue to `let' +and `let*' but create bindings that are evaluated lazily. + * New Modes and Packages in Emacs 27.1 diff --git a/lisp/emacs-lisp/thunk.el b/lisp/emacs-lisp/thunk.el index 371d10444b..9cb16857f4 100644 --- a/lisp/emacs-lisp/thunk.el +++ b/lisp/emacs-lisp/thunk.el @@ -41,6 +41,10 @@ ;; following: ;; ;; (thunk-force delayed) +;; +;; This file also defines macros `thunk-let' and `thunk-let*' that are +;; analogous to `let' and `let*' but provide lazy evaluation of +;; bindings by using thunks implicitly (i.e. in the expansion). ;;; Code: @@ -71,5 +75,57 @@ thunk-evaluated-p "Return non-nil if DELAYED has been evaluated." (funcall delayed t)) +(defmacro thunk-let (bindings &rest body) + "Like `let' but create lazy bindings. + +BINDINGS is a list of elements of the form (SYMBOL EXPRESSION). +Any binding EXPRESSION is not evaluated before the variable +SYMBOL is used for the first time when evaluating the BODY. + +It is not allowed to set `thunk-let' or `thunk-let*' bound +variables. + +Using `thunk-let' and `thunk-let*' requires `lexical-binding'." + (declare (indent 1) (debug let)) + (cl-callf2 mapcar + (lambda (binding) + (pcase binding + (`(,(pred symbolp) ,_) binding) + (_ (signal 'error (cons "Bad binding in thunk-let" + (list binding)))))) + bindings) + (cl-callf2 mapcar + (pcase-lambda (`(,var ,binding)) + (list (make-symbol (concat (symbol-name var) "-thunk")) + var binding)) + bindings) + `(let ,(mapcar + (pcase-lambda (`(,thunk-var ,_var ,binding)) + `(,thunk-var (thunk-delay ,binding))) + bindings) + (cl-symbol-macrolet + ,(mapcar (pcase-lambda (`(,thunk-var ,var ,_binding)) + `(,var (thunk-force ,thunk-var))) + bindings) + ,@body))) + +(defmacro thunk-let* (bindings &rest body) + "Like `let*' but create lazy bindings. + +BINDINGS is a list of elements of the form (SYMBOL EXPRESSION). +Any binding EXPRESSION is not evaluated before the variable +SYMBOL is used for the first time when evaluating the BODY. + +It is not allowed to set `thunk-let' or `thunk-let*' bound +variables. + +Using `thunk-let' and `thunk-let*' requires `lexical-binding'." + (declare (indent 1) (debug let)) + (cl-reduce + (lambda (expr binding) `(thunk-let (,binding) ,expr)) + (nreverse bindings) + :initial-value (macroexp-progn body))) + + (provide 'thunk) ;;; thunk.el ends here diff --git a/test/lisp/emacs-lisp/thunk-tests.el b/test/lisp/emacs-lisp/thunk-tests.el index 973a14b818..e122098b16 100644 --- a/test/lisp/emacs-lisp/thunk-tests.el +++ b/test/lisp/emacs-lisp/thunk-tests.el @@ -51,5 +51,55 @@ (thunk-force thunk) (should (= x 1)))) + + +;; thunk-let tests + +(ert-deftest thunk-let-basic-test () + "Test whether bindings are established." + (should (equal (thunk-let ((x 1) (y 2)) (+ x y)) 3))) + +(ert-deftest thunk-let*-basic-test () + "Test whether bindings are established." + (should (equal (thunk-let* ((x 1) (y (+ 1 x))) (+ x y)) 3))) + +(ert-deftest thunk-let-bound-vars-cant-be-set-test () + "Test whether setting a `thunk-let' bound variable fails." + (should-error + (eval '(thunk-let ((x 1)) (let ((y 7)) (setq x (+ x y)) (* 10 x))) t))) + +(ert-deftest thunk-let-laziness-test () + "Test laziness of `thunk-let'." + (should + (equal (let ((x-evalled nil) + (y-evalled nil)) + (thunk-let ((x (progn (setq x-evalled t) (+ 1 2))) + (y (progn (setq y-evalled t) (+ 3 4)))) + (let ((evalled-y y)) + (list x-evalled y-evalled evalled-y)))) + (list nil t 7)))) + +(ert-deftest thunk-let*-laziness-test () + "Test laziness of `thunk-let*'." + (should + (equal (let ((x-evalled nil) + (y-evalled nil) + (z-evalled nil) + (a-evalled nil)) + (thunk-let* ((x (progn (setq x-evalled t) (+ 1 1))) + (y (progn (setq y-evalled t) (+ x 1))) + (z (progn (setq z-evalled t) (+ y 1))) + (a (progn (setq a-evalled t) (+ z 1)))) + (let ((evalled-z z)) + (list x-evalled y-evalled z-evalled a-evalled evalled-z)))) + (list t t t nil 4)))) + +(ert-deftest thunk-let-bad-binding-test () + "Test whether a bad binding causes a compiler error." + (should-error (byte-compile (thunk-let ((x 1 1)) x))) + (should-error (byte-compile (thunk-let (27) x))) + (should-error (byte-compile (thunk-let x x)))) + + (provide 'thunk-tests) ;;; thunk-tests.el ends here -- 2.15.0 --=-=-= Content-Type: text/plain Is this ok to install now? @Eli: I think we can still add the words I wrote to the manual. What is the cheapest way to learn enough of texinfo and its usage by the Emacs manual to be able to create a correct patch? Thanks, Michael. --=-=-=--