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: Wed, 08 Nov 2017 18:22:51 +0100 [thread overview]
Message-ID: <87o9ocd6s4.fsf@web.de> (raw)
In-Reply-To: <jwvpo9wbag9.fsf-monnier+emacs@gnu.org> (Stefan Monnier's message of "Mon, 09 Oct 2017 11:38:47 -0400")
[-- Attachment #1: Type: text/plain, Size: 513 bytes --]
Stefan Monnier <monnier@iro.umontreal.ca> 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)?
[-- 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: 6489 bytes --]
From 4941becd07f6ffbe387006248193d95b258be526 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.
* 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.
\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..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")))
+\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: 6489 bytes --]
From 4941becd07f6ffbe387006248193d95b258be526 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.
* 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.
\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..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")))
+\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.
next prev parent reply other threads:[~2017-11-08 17:22 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 [this message]
2017-11-08 18:02 ` Stefan Monnier
2017-11-09 15:14 ` Michael Heerdegen
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=87o9ocd6s4.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).