unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Michael Heerdegen <michael_heerdegen@web.de>
To: Stefan Monnier <monnier@IRO.UMontreal.CA>
Cc: Nicolas Petton <nicolas@petton.fr>,
	Emacs Development <emacs-devel@gnu.org>
Subject: Re: [SUSPECTED SPAM] Re: `thunk-let'?
Date: Thu, 09 Nov 2017 16:14:45 +0100	[thread overview]
Message-ID: <87wp2zcwm2.fsf@web.de> (raw)
In-Reply-To: <jwvr2t8648w.fsf-monnier+emacs@gnu.org> (Stefan Monnier's message of "Wed, 08 Nov 2017 13:02:59 -0500")

[-- Attachment #1: Type: text/plain, Size: 690 bytes --]

Stefan Monnier <monnier@IRO.UMontreal.CA> writes:

> I think lazily binding a variable to nil is useless, so I'd drop the
> first pcase branch above.

Makes sense -- done.


> > +   :initial-value `(progn ,@body)))
>
> You can use `macroexp-progn` to avoid constructing a `progn` in the
> common case where body is already a single expression.

Done (though the byte compiler already dismissed this redundant `progn'
in every case, so I didn't care about it).


> > +;;;###autoload
> >  (defmacro thunk-delay (&rest body)
>
> Maybe a better option is to add a `require` in the expansion of lazy-let?

That should be fine, so I've done it as well.


This is the updated (cumulative) patch:


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-macros-lazy-let-and-lazy-let.patch --]
[-- Type: text/x-diff, Size: 5842 bytes --]

From f8bb4d2872c366af45032a3d8d1d2e907c3e4d9d Mon Sep 17 00:00:00 2001
From: Michael Heerdegen <michael_heerdegen@web.de>
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.
* 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                             |  3 +++
 lisp/emacs-lisp/subr-x.el            | 49 +++++++++++++++++++++++++++++++++++
 test/lisp/emacs-lisp/subr-x-tests.el | 50 +++++++++++++++++++++++++++++++++++-
 3 files changed, 101 insertions(+), 1 deletion(-)

diff --git a/etc/NEWS b/etc/NEWS
index c47ca42d27..451688c665 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -128,6 +128,9 @@ calling 'eldoc-message' directly.
 \f
 * 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..8e64cbd4ce 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -245,6 +245,55 @@ string-remove-suffix
       (substring string 0 (- (length string) (length suffix)))
     string))
 
+(defmacro lazy-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.
+
+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
+          (`(,(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)
+  `(progn
+     (require 'thunk)
+     (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 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.
+
+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 (macroexp-progn body)))
+
 (provide 'subr-x)
 
 ;;; subr-x.el ends here
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")))
 
+\f
+;; 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


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-Add-macros-lazy-let-and-lazy-let.patch --]
[-- Type: text/x-diff, Size: 5842 bytes --]

From f8bb4d2872c366af45032a3d8d1d2e907c3e4d9d Mon Sep 17 00:00:00 2001
From: Michael Heerdegen <michael_heerdegen@web.de>
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.
* 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                             |  3 +++
 lisp/emacs-lisp/subr-x.el            | 49 +++++++++++++++++++++++++++++++++++
 test/lisp/emacs-lisp/subr-x-tests.el | 50 +++++++++++++++++++++++++++++++++++-
 3 files changed, 101 insertions(+), 1 deletion(-)

diff --git a/etc/NEWS b/etc/NEWS
index c47ca42d27..451688c665 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -128,6 +128,9 @@ calling 'eldoc-message' directly.
 \f
 * 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..8e64cbd4ce 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -245,6 +245,55 @@ string-remove-suffix
       (substring string 0 (- (length string) (length suffix)))
     string))
 
+(defmacro lazy-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.
+
+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
+          (`(,(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)
+  `(progn
+     (require 'thunk)
+     (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 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.
+
+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 (macroexp-progn body)))
+
 (provide 'subr-x)
 
 ;;; subr-x.el ends here
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")))
 
+\f
+;; 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


[-- Attachment #4: Type: text/plain, Size: 20 bytes --]



Thanks,

Michael.

  reply	other threads:[~2017-11-09 15:14 UTC|newest]

Thread overview: 77+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-10-08 20:12 `thunk-let'? Michael Heerdegen
2017-10-08 22:25 ` `thunk-let'? Michael Heerdegen
2017-10-09  3:10 ` `thunk-let'? Stefan Monnier
2017-10-09 11:40   ` `thunk-let'? Michael Heerdegen
2017-10-09 14:07     ` `thunk-let'? Michael Heerdegen
2017-10-09 14:27       ` `thunk-let'? Michael Heerdegen
2017-10-09 15:38     ` [SUSPECTED SPAM] `thunk-let'? Stefan Monnier
2017-11-08 17:22       ` Michael Heerdegen
2017-11-08 18:02         ` Stefan Monnier
2017-11-09 15:14           ` Michael Heerdegen [this message]
2017-11-09 18:39             ` `thunk-let'? Michael Heerdegen
2017-11-09 18:48               ` `thunk-let'? Stefan Monnier
2017-11-22  2:50                 ` `thunk-let'? Michael Heerdegen
2017-11-22  3:43                   ` `thunk-let'? Eli Zaretskii
2017-11-22 16:16                     ` `thunk-let'? Eli Zaretskii
2017-11-22 19:25                       ` `thunk-let'? Michael Heerdegen
2017-11-22 20:00                         ` `thunk-let'? Eli Zaretskii
2017-11-23  2:59                       ` `thunk-let'? Michael Heerdegen
2017-11-23  4:15                         ` `thunk-let'? Michael Heerdegen
2017-11-23 16:34                           ` `thunk-let'? Pip Cet
2017-11-23 23:41                             ` `thunk-let'? Michael Heerdegen
2017-11-24  8:37                               ` `thunk-let'? Eli Zaretskii
2017-11-24  8:51                                 ` `thunk-let'? Stefan Monnier
2017-11-24  9:16                                   ` `thunk-let'? Eli Zaretskii
2017-11-24 13:33                                     ` `thunk-let'? Stefan Monnier
2017-11-27  5:21                                 ` `thunk-let'? Michael Heerdegen
2017-11-27 13:34                                   ` `thunk-let'? Stefan Monnier
2017-11-27 15:44                                     ` `thunk-let'? Eli Zaretskii
2017-11-30 15:19                                       ` `thunk-let'? Michael Heerdegen
2017-11-24  8:36                           ` `thunk-let'? Eli Zaretskii
2017-11-30 15:17                             ` `thunk-let'? Michael Heerdegen
2017-11-30 16:06                               ` `thunk-let'? Eli Zaretskii
2017-12-01  8:02                                 ` `thunk-let'? Michael Heerdegen
2017-11-23 16:04                         ` `thunk-let'? Eli Zaretskii
2017-11-22 17:44                   ` `thunk-let'? Gemini Lasswell
2017-11-22 18:04                     ` `thunk-let'? Noam Postavsky
2017-11-22 18:31                       ` `thunk-let'? Michael Heerdegen
2017-11-22 18:29                     ` `thunk-let'? Michael Heerdegen
2017-11-22 19:54                     ` `thunk-let'? Stefan Monnier
2017-11-22 22:47                       ` `thunk-let'? Michael Heerdegen
2017-11-10 10:01             ` [SUSPECTED SPAM] `thunk-let'? Eli Zaretskii
2017-11-08 18:04         ` Eli Zaretskii
2017-11-08 22:22           ` `thunk-let'? Michael Heerdegen
2017-11-08 23:06             ` `thunk-let'? Drew Adams
2017-11-09 17:20             ` `thunk-let'? Eli Zaretskii
2017-11-09 17:39               ` `thunk-let'? Clément Pit-Claudel
2017-11-09 18:06                 ` `thunk-let'? Michael Heerdegen
2017-11-09 21:05                   ` `thunk-let'? Drew Adams
2017-11-09 23:07                     ` Sandbox subr-x? (was: `thunk-let'?) Michael Heerdegen
2017-11-09 23:54                       ` Drew Adams
2017-11-10  7:57                       ` Eli Zaretskii
2017-11-09 21:48                   ` `thunk-let'? Clément Pit-Claudel
2017-11-09 22:43                     ` `thunk-let'? Michael Heerdegen
2017-11-10  7:48                     ` `thunk-let'? Eli Zaretskii
2017-11-09 18:14               ` `thunk-let'? Michael Heerdegen
2017-11-09 20:26                 ` `thunk-let'? Eli Zaretskii
2017-11-09 23:13                   ` `thunk-let'? Michael Heerdegen
2017-11-10  7:58                     ` `thunk-let'? Eli Zaretskii
2017-11-11 15:20                       ` `thunk-let'? Michael Heerdegen
2017-11-11 15:40                         ` `thunk-let'? Eli Zaretskii
2017-11-10 10:10               ` `thunk-let'? Nicolas Petton
2017-11-09 14:34           ` [SUSPECTED SPAM] `thunk-let'? Michael Heerdegen
2017-11-09 17:12             ` Eli Zaretskii
2017-11-09 15:19           ` Michael Heerdegen
2017-10-09  8:00 ` `thunk-let'? Nicolas Petton
2017-12-08 20:38 ` A generalization of `thunk-let' (was: `thunk-let'?) Michael Heerdegen
2017-12-08 21:16   ` A generalization of `thunk-let' Stefan Monnier
2017-12-09 10:33     ` Michael Heerdegen
2017-12-10  4:47       ` Stefan Monnier
2017-12-10  5:34         ` John Wiegley
2017-12-12 14:41         ` Michael Heerdegen
2017-12-13 13:52           ` Michael Heerdegen
2017-12-13 14:09             ` Stefan Monnier
2017-12-13 14:37               ` Michael Heerdegen
2018-01-12 20:03     ` Michael Heerdegen
2017-12-09 21:59   ` A generalization of `thunk-let' (was: `thunk-let'?) Richard Stallman
2017-12-10 17:03     ` A generalization of `thunk-let' Michael Heerdegen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87wp2zcwm2.fsf@web.de \
    --to=michael_heerdegen@web.de \
    --cc=emacs-devel@gnu.org \
    --cc=monnier@IRO.UMontreal.CA \
    --cc=nicolas@petton.fr \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).