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: [SUSPECTED SPAM] Re: `thunk-let'? Date: Wed, 08 Nov 2017 18:22:51 +0100 Message-ID: <87o9ocd6s4.fsf@web.de> References: <87infp9z6j.fsf@web.de> <87zi90eehg.fsf@web.de> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1510161828 28817 195.159.176.226 (8 Nov 2017 17:23:48 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Wed, 8 Nov 2017 17:23:48 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (gnu/linux) Cc: Nicolas Petton , Emacs Development To: Stefan Monnier Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Wed Nov 08 18:23:44 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 1eCU4j-0007Bo-Os for ged-emacs-devel@m.gmane.org; Wed, 08 Nov 2017 18:23:42 +0100 Original-Received: from localhost ([::1]:32860 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eCU4r-0004UB-7F for ged-emacs-devel@m.gmane.org; Wed, 08 Nov 2017 12:23:49 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:34928) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eCU4C-0004U3-As for emacs-devel@gnu.org; Wed, 08 Nov 2017 12:23:13 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eCU48-0003yL-W3 for emacs-devel@gnu.org; Wed, 08 Nov 2017 12:23:08 -0500 Original-Received: from mout.web.de ([212.227.17.12]:51221) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1eCU48-0003xn-FP for emacs-devel@gnu.org; Wed, 08 Nov 2017 12:23:04 -0500 Original-Received: from drachen.dragon ([94.217.120.125]) by smtp.web.de (mrweb101 [213.165.67.124]) with ESMTPSA (Nemesis) id 0MYw63-1egTVP0JPP-00VgoW; Wed, 08 Nov 2017 18:22:52 +0100 In-Reply-To: (Stefan Monnier's message of "Mon, 09 Oct 2017 11:38:47 -0400") X-Provags-ID: V03:K0:aisJylKbKgdXkaxAeGIr7TdvHTd2A6KLWXlxOXWFNB2NhCUmfxk v6dlZdhZFNvJJSo4ycyejPTLmd4DYW7G3M24Npvn0fZqruZR5DuhkENr/YaKCnnxuscKScg CEvQ9r7hxqSnlE7JjKTAevAsqWNQOg+1tU8xRc50FyYfO5f9hvTGDc+xE+jVZ29ZMJi0qUW JfFPcP5KxRj4oisy2pBGQ== X-UI-Out-Filterresults: notjunk:1;V01:K0:TiJn3OWMSRg=:Tbi9zsVvrlK7aMVd7X4ymW URu/COYHsDPHGjcBCsBByNdLum1fbbqK0tpGXbRhuQP/NRkQ24fHJ8lR2CIXlC628gxwKyRPS cPk39Jipzz/+flovwnDdAQMzAUM1yLGYcgpL+1BeQpjJ/ivfHP9rnS0glC7uBxQNWv4jJckYT YGapO1sYv+VEAOeW9hl9Lil2HzRJQTH0ByRo2uwCHRQpt0TTGG+4nSs0jFLE0xkHYT9F/pbFX cti79tdQf6dH8icPPWUbfXxs/LNzsNLWzDBiuM/9N+HtHzxMKsiDSmFX4y2EZ45ynYhxu0QdB HNSv5iV7zQQTVrzHHZjT1yg3c7RWRZsNGi7tl3/FIx5XZ9JpAk7DvPKnZLZ4hu9cLYECOEehZ JR0kqMpeWKqw/5TWIlsayHbcZOQTrI2xBijyfyfShLykdNSVbEwXljrc945Gue36TdSGIaK3R SrdD339xHegKEh+vvo6uf7eIcnueMtUB3S84EA+sHmNphrEmadSCx19n3OaRhRXTPk8STPDQY Y2ssO8CCiP5VklfEWlMBkcOauz6/M64nzXn8TlOt3RhaznfSUNGr+E/6v/dZzw7vlXoGOF3uZ L6CfNnf5INHkRnD6tHYMcSiAVHQ/nkdR1LrpyNRBwXOUHT0EYaAwEVM7P3nEVt1p+AjWy2O5e O9acl5X9sDfrll9bk/U/CcvsiGpgdLa8KV+QFbeHsjwXmu/TzzBtYwj3JshQo6LxCXmRbyIP7 3K//FQdyobURllEuAHxIqlPOi1IBbv1OANQo/YdReNd9lizeFkf50kiKdKKYV60R92KySXUa X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 212.227.17.12 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:219983 Archived-At: --=-=-= Content-Type: text/plain Stefan Monnier writes: > > Ok, so let's be brave and aim to add it as `lazy-let' to subr-x. > > There is a question I want us to think about: what should the semantics > > of a `lazy-let' bound variable be if it is bound or set inside the > > BODY? > > The semantics of `setq`ing such a var should be: compile-time error. > The semantics of let-rebinding such a variable should be for the new > binding to hide the outer (lazy) one. Is this patch acceptable (also attached as file)? --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=0001-Add-macros-lazy-let-and-lazy-let.patch >From 4941becd07f6ffbe387006248193d95b258be526 Mon Sep 17 00:00:00 2001 From: Michael Heerdegen Date: Thu, 2 Nov 2017 18:45:34 +0100 Subject: [PATCH] Add macros `lazy-let' and `lazy-let*' * lisp/emacs-lisp/subr-x.el (lazy-let, lazy-let*): New macros. * lisp/emacs-lisp/thunk.el (thunk-delay, thunk-force): Add autoload cookies. * test/lisp/emacs-lisp/subr-x-tests.el: Use lexical-binding. (subr-x-lazy-let-basic-test, subr-x-lazy-let*-basic-test) (subr-x-lazy-let-bound-vars-cant-be-bound-test) (subr-x-lazy-let-lazyness-test, subr-x-lazy-let*-lazyness-test) (subr-x-lazy-let-bad-binding-test): New tests for `lazy-let' and `lazy-let*. --- etc/NEWS | 4 +++ lisp/emacs-lisp/subr-x.el | 48 ++++++++++++++++++++++++++++++++++ lisp/emacs-lisp/thunk.el | 2 ++ test/lisp/emacs-lisp/subr-x-tests.el | 50 +++++++++++++++++++++++++++++++++++- 4 files changed, 103 insertions(+), 1 deletion(-) diff --git a/etc/NEWS b/etc/NEWS index c47ca42d27..8b1f659ebf 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -128,6 +128,10 @@ calling 'eldoc-message' directly. * Lisp Changes in Emacs 27.1 +--- +** The new macros 'lazy-let' and 'lazy-let*' are analogue to `let' and +`let*' but create bindings that are evaluated lazily. + --- ** The 'file-system-info' function is now available on all platforms. instead of just Microsoft platforms. This fixes a 'get-free-disk-space' diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index 8ed29d8659..ce8956c96f 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -245,6 +245,54 @@ string-remove-suffix (substring string 0 (- (length string) (length suffix))) string)) +(defmacro lazy-let (bindings &rest body) + "Like `let' but make delayed bindings. + +This is like `let' but any binding expression is not evaluated +before the variable is used for the first time. + +It is not allowed to set `lazy-let' or `lazy-let*' bound +variables." + (declare (indent 1) (debug let)) + (cl-callf2 mapcar + (lambda (binding) + (pcase binding + ((or (and (pred symbolp) s) + `(,(and (pred symbolp) s))) + `(,s nil)) + (`(,(pred symbolp) ,_) binding) + (_ (signal 'error (cons "Bad binding in lazy-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 lazy-let* (bindings &rest body) + "Like `let*' but make delayed bindings. + +This is like `let*' but any binding expression is not evaluated +before the variable is used for the first time. + +It is not allowed to set `lazy-let' or `lazy-let*' bound +variables." + (declare (indent 1) (debug let)) + (cl-reduce + (lambda (expr binding) `(lazy-let (,binding) ,expr)) + (nreverse bindings) + :initial-value `(progn ,@body))) + (provide 'subr-x) ;;; subr-x.el ends here diff --git a/lisp/emacs-lisp/thunk.el b/lisp/emacs-lisp/thunk.el index 371d10444b..0c5d0b709e 100644 --- a/lisp/emacs-lisp/thunk.el +++ b/lisp/emacs-lisp/thunk.el @@ -46,6 +46,7 @@ (eval-when-compile (require 'cl-macs)) +;;;###autoload (defmacro thunk-delay (&rest body) "Delay the evaluation of BODY." (declare (debug t)) @@ -61,6 +62,7 @@ thunk-delay (setf ,forced t)) ,val))))) +;;;###autoload (defun thunk-force (delayed) "Force the evaluation of DELAYED. The result is cached and will be returned on subsequent calls diff --git a/test/lisp/emacs-lisp/subr-x-tests.el b/test/lisp/emacs-lisp/subr-x-tests.el index 0e8871d9a9..c477a63a29 100644 --- a/test/lisp/emacs-lisp/subr-x-tests.el +++ b/test/lisp/emacs-lisp/subr-x-tests.el @@ -1,4 +1,4 @@ -;;; subr-x-tests.el --- Testing the extended lisp routines +;;; subr-x-tests.el --- Testing the extended lisp routines -*- lexical-binding: t -*- ;; Copyright (C) 2014-2017 Free Software Foundation, Inc. @@ -538,6 +538,54 @@ (format "abs sum is: %s")) "abs sum is: 15"))) + +;; lazy-let tests + +(ert-deftest subr-x-lazy-let-basic-test () + "Test whether bindings are established." + (should (equal (lazy-let ((x 1) (y 2)) (+ x y)) 3))) + +(ert-deftest subr-x-lazy-let*-basic-test () + "Test whether bindings are established." + (should (equal (lazy-let* ((x 1) (y (+ 1 x))) (+ x y)) 3))) + +(ert-deftest subr-x-lazy-let-bound-vars-cant-be-bound-test () + "Test whether setting or binding a `lazy-let' bound variable fails." + (should-error (eval '(lazy-let ((x 1)) (let ((y 7)) (setq x (+ x y)) (* 10 x))) t)) + (should-error (eval '(lazy-let ((x 1)) (let ((x 2)) x)) t))) + +(ert-deftest subr-x-lazy-let-lazyness-test () + "Test for lazyness." + (should + (equal (let ((x-evalled nil) + (y-evalled nil)) + (lazy-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 subr-x-lazy-let*-lazyness-test () + "Test lazyness of `lazy-let*'." + (should + (equal (let ((x-evalled nil) + (y-evalled nil) + (z-evalled nil) + (a-evalled nil)) + (lazy-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 subr-x-lazy-let-bad-binding-test () + "Test whether a bad binding causes a compiler error." + (should-error (byte-compile (lazy-let ((x 1 1)) x))) + (should-error (byte-compile (lazy-let (27) x))) + (should-error (byte-compile (lazy-let x x)))) + (provide 'subr-x-tests) ;;; subr-x-tests.el ends here -- 2.14.2 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Add-macros-lazy-let-and-lazy-let.patch >From 4941becd07f6ffbe387006248193d95b258be526 Mon Sep 17 00:00:00 2001 From: Michael Heerdegen Date: Thu, 2 Nov 2017 18:45:34 +0100 Subject: [PATCH] Add macros `lazy-let' and `lazy-let*' * lisp/emacs-lisp/subr-x.el (lazy-let, lazy-let*): New macros. * lisp/emacs-lisp/thunk.el (thunk-delay, thunk-force): Add autoload cookies. * test/lisp/emacs-lisp/subr-x-tests.el: Use lexical-binding. (subr-x-lazy-let-basic-test, subr-x-lazy-let*-basic-test) (subr-x-lazy-let-bound-vars-cant-be-bound-test) (subr-x-lazy-let-lazyness-test, subr-x-lazy-let*-lazyness-test) (subr-x-lazy-let-bad-binding-test): New tests for `lazy-let' and `lazy-let*. --- etc/NEWS | 4 +++ lisp/emacs-lisp/subr-x.el | 48 ++++++++++++++++++++++++++++++++++ lisp/emacs-lisp/thunk.el | 2 ++ test/lisp/emacs-lisp/subr-x-tests.el | 50 +++++++++++++++++++++++++++++++++++- 4 files changed, 103 insertions(+), 1 deletion(-) diff --git a/etc/NEWS b/etc/NEWS index c47ca42d27..8b1f659ebf 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -128,6 +128,10 @@ calling 'eldoc-message' directly. * Lisp Changes in Emacs 27.1 +--- +** The new macros 'lazy-let' and 'lazy-let*' are analogue to `let' and +`let*' but create bindings that are evaluated lazily. + --- ** The 'file-system-info' function is now available on all platforms. instead of just Microsoft platforms. This fixes a 'get-free-disk-space' diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index 8ed29d8659..ce8956c96f 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -245,6 +245,54 @@ string-remove-suffix (substring string 0 (- (length string) (length suffix))) string)) +(defmacro lazy-let (bindings &rest body) + "Like `let' but make delayed bindings. + +This is like `let' but any binding expression is not evaluated +before the variable is used for the first time. + +It is not allowed to set `lazy-let' or `lazy-let*' bound +variables." + (declare (indent 1) (debug let)) + (cl-callf2 mapcar + (lambda (binding) + (pcase binding + ((or (and (pred symbolp) s) + `(,(and (pred symbolp) s))) + `(,s nil)) + (`(,(pred symbolp) ,_) binding) + (_ (signal 'error (cons "Bad binding in lazy-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 lazy-let* (bindings &rest body) + "Like `let*' but make delayed bindings. + +This is like `let*' but any binding expression is not evaluated +before the variable is used for the first time. + +It is not allowed to set `lazy-let' or `lazy-let*' bound +variables." + (declare (indent 1) (debug let)) + (cl-reduce + (lambda (expr binding) `(lazy-let (,binding) ,expr)) + (nreverse bindings) + :initial-value `(progn ,@body))) + (provide 'subr-x) ;;; subr-x.el ends here diff --git a/lisp/emacs-lisp/thunk.el b/lisp/emacs-lisp/thunk.el index 371d10444b..0c5d0b709e 100644 --- a/lisp/emacs-lisp/thunk.el +++ b/lisp/emacs-lisp/thunk.el @@ -46,6 +46,7 @@ (eval-when-compile (require 'cl-macs)) +;;;###autoload (defmacro thunk-delay (&rest body) "Delay the evaluation of BODY." (declare (debug t)) @@ -61,6 +62,7 @@ thunk-delay (setf ,forced t)) ,val))))) +;;;###autoload (defun thunk-force (delayed) "Force the evaluation of DELAYED. The result is cached and will be returned on subsequent calls diff --git a/test/lisp/emacs-lisp/subr-x-tests.el b/test/lisp/emacs-lisp/subr-x-tests.el index 0e8871d9a9..c477a63a29 100644 --- a/test/lisp/emacs-lisp/subr-x-tests.el +++ b/test/lisp/emacs-lisp/subr-x-tests.el @@ -1,4 +1,4 @@ -;;; subr-x-tests.el --- Testing the extended lisp routines +;;; subr-x-tests.el --- Testing the extended lisp routines -*- lexical-binding: t -*- ;; Copyright (C) 2014-2017 Free Software Foundation, Inc. @@ -538,6 +538,54 @@ (format "abs sum is: %s")) "abs sum is: 15"))) + +;; lazy-let tests + +(ert-deftest subr-x-lazy-let-basic-test () + "Test whether bindings are established." + (should (equal (lazy-let ((x 1) (y 2)) (+ x y)) 3))) + +(ert-deftest subr-x-lazy-let*-basic-test () + "Test whether bindings are established." + (should (equal (lazy-let* ((x 1) (y (+ 1 x))) (+ x y)) 3))) + +(ert-deftest subr-x-lazy-let-bound-vars-cant-be-bound-test () + "Test whether setting or binding a `lazy-let' bound variable fails." + (should-error (eval '(lazy-let ((x 1)) (let ((y 7)) (setq x (+ x y)) (* 10 x))) t)) + (should-error (eval '(lazy-let ((x 1)) (let ((x 2)) x)) t))) + +(ert-deftest subr-x-lazy-let-lazyness-test () + "Test for lazyness." + (should + (equal (let ((x-evalled nil) + (y-evalled nil)) + (lazy-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 subr-x-lazy-let*-lazyness-test () + "Test lazyness of `lazy-let*'." + (should + (equal (let ((x-evalled nil) + (y-evalled nil) + (z-evalled nil) + (a-evalled nil)) + (lazy-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 subr-x-lazy-let-bad-binding-test () + "Test whether a bad binding causes a compiler error." + (should-error (byte-compile (lazy-let ((x 1 1)) x))) + (should-error (byte-compile (lazy-let (27) x))) + (should-error (byte-compile (lazy-let x x)))) + (provide 'subr-x-tests) ;;; subr-x-tests.el ends here -- 2.14.2 --=-=-= Content-Type: text/plain Thanks, Michael. --=-=-=--