From: Michael Heerdegen <michael_heerdegen@web.de>
To: Eli Zaretskii <eliz@gnu.org>
Cc: emacs-devel@gnu.org
Subject: Re: `thunk-let'?
Date: Thu, 23 Nov 2017 05:15:44 +0100 [thread overview]
Message-ID: <873755hbpr.fsf@web.de> (raw)
In-Reply-To: <87a7zdbsyf.fsf@web.de> (Michael Heerdegen's message of "Thu, 23 Nov 2017 03:59:52 +0100")
[-- Attachment #1: Type: text/plain, Size: 334 bytes --]
Michael Heerdegen <michael_heerdegen@web.de> writes:
> This helped a lot. My first version even compiled without error. I'll
> post the updated patch soon.
Ok, here is what I have, with everything discusses included (see also
attached). I will proofread it once more, maybe others will want to
have a look in the meantime, too.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-macros-thunk-let-and-thunk-let.patch --]
[-- Type: text/x-diff, Size: 11083 bytes --]
From 6d7028defb30b403505a72ecf68ac52fe478d562 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 `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*.
* doc/lispref/eval.texi (Deferred Eval): New section.
* doc/lispref/elisp.texi: Update menu.
---
doc/lispref/elisp.texi | 1 +
doc/lispref/eval.texi | 118 ++++++++++++++++++++++++++++++++++--
etc/NEWS | 4 ++
lisp/emacs-lisp/thunk.el | 56 +++++++++++++++++
test/lisp/emacs-lisp/thunk-tests.el | 50 +++++++++++++++
5 files changed, 224 insertions(+), 5 deletions(-)
diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi
index c752594584..a271749e04 100644
--- a/doc/lispref/elisp.texi
+++ b/doc/lispref/elisp.texi
@@ -455,6 +455,7 @@ Top
the program).
* Backquote:: Easier construction of list structure.
* Eval:: How to invoke the Lisp interpreter explicitly.
+* Deferred Eval:: Deferred and lazy evaluation of forms.
Kinds of Forms
diff --git a/doc/lispref/eval.texi b/doc/lispref/eval.texi
index 064fca22ff..8b61e39142 100644
--- a/doc/lispref/eval.texi
+++ b/doc/lispref/eval.texi
@@ -20,11 +20,12 @@ Evaluation
@ifnottex
@menu
-* Intro Eval:: Evaluation in the scheme of things.
-* Forms:: How various sorts of objects are evaluated.
-* Quoting:: Avoiding evaluation (to put constants in the program).
-* Backquote:: Easier construction of list structure.
-* Eval:: How to invoke the Lisp interpreter explicitly.
+* Intro Eval:: Evaluation in the scheme of things.
+* Forms:: How various sorts of objects are evaluated.
+* Quoting:: Avoiding evaluation (to put constants in the program).
+* Backquote:: Easier construction of list structure.
+* Eval:: How to invoke the Lisp interpreter explicitly.
+* Deferred Eval:: Deferred and lazy evaluation of forms
@end menu
@node Intro Eval
@@ -877,3 +878,110 @@ Eval
@end group
@end example
@end defvar
+
+@node Deferred Eval
+@section Deferred and Lazy Evaluation
+
+@cindex deferred evaluation
+@cindex lazy evaluation
+
+
+ Sometimes it is useful to delay the evaluation of an expression, for
+example if you want to avoid to perform a time-consuming calculation
+in the case that it turns out that the result is not needed in the
+future of the program.
+
+
+@defmac thunk-delay forms...
+Return a thunk for evaluating the @var{forms}. A thunk is a closure
+that evaluates the @var{forms} in the lexical environment present when
+@code{thunk-delay} had been called. Using this macro requires
+@code{lexical-binding}.
+@end defmac
+
+@defun thunk-force thunk
+Force @var{thunk} to perform the evaluation of the forms specified to the
+@code{thunk-delay} that created the thunk. The result of the evaluation
+of the last form is returned. The @var{thunk} also "remembers" that it has
+been forced: Any further calls of @code{thunk-force} on the same @var{thunk}
+will just return the same result without evaluating the @var{forms} again.
+@end defun
+
+@defmac lazy-let (bindings...) forms...
+This macro is analogous to @code{let} but creates "lazy" variable
+bindings. Any binding has the form (@var{symbol} @var{value-form}).
+Unlike @code{let}, the evaluation of any @var{value-form} is deferred
+until the binding of the according @var{symbol} is used for the first
+time when evaluating the @var{forms}. Any @var{value-form} is evaluated
+at most once. Using this macro requires @code{lexical-binding}.
+@end defmac
+
+@example
+@group
+(defun f (number)
+ (lazy-let ((derived-number
+ (progn (message "Calculating 1 plus 2 times %d" number)
+ (1+ (* 2 number)))))
+ (if (> number 10)
+ derived-number
+ number)))
+@end group
+
+@group
+(f 5)
+@result{} 5
+@end group
+
+@group
+(f 12)
+@print{} "Calculating 1 plus 2 times 12"
+25
+@end group
+
+@end example
+
+Because of the special nature of lazily bound variables, it is an error
+to set them (e.g.@ with @code{setq}).
+
+
+@defmac lazy-let* (bindings...) forms...
+This is like @code{lazy-let} but any expression in @var{bindings} is allowed
+to refer to preceding bindings in this @code{lazy-let*} form. Using
+this macro requires @code{lexical-binding}.
+@end defmac
+
+@example
+@group
+(lazy-let* ((x (prog2 (message "Calculating x...")
+ (+ 1 1)
+ (message "Finished calculating x")))
+ (y (prog2 (message "Calculating y...")
+ (+ x 1)
+ (message "Finished calculating y")))
+ (z (prog2 (message "Calculating z...")
+ (+ y 1)
+ (message "Finished calculating z")))
+ (a (prog2 (message "Calculating a...")
+ (+ z 1)
+ (message "Finished calculating a"))))
+ (* z x))
+
+@print{} Calculating z...
+@print{} Calculating y...
+@print{} Calculating x...
+@print{} Finished calculating x
+@print{} Finished calculating y
+@print{} Finished calculating z
+@result{} 8
+
+@end group
+@end example
+
+@code{lazy-let} and @code{lazy-let*} use thunks implicitly: their
+expansion creates helper symbols and binds them to thunks wrapping the
+binding expressions. All references to the original variables in the
+body @var{forms} are then replaced by an expression that calls
+@code{thunk-force} on the according helper variable. So, any code
+using @code{lazy-let} or @code{lazy-let*} could be rewritten to use
+thunks, but in many cases using these macros results in nicer code
+than using thunks explicitly.
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.
+
\f
* 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..8e5c8fd4d5 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))))
+
+\f
+;; 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 (macroexpand '(thunk-let ((x 1 1)) x)))
+ (should-error (macroexpand '(thunk-let (27) x)))
+ (should-error (macroexpand '(thunk-let x x))))
+
+
(provide 'thunk-tests)
;;; thunk-tests.el ends here
--
2.15.0
[-- Attachment #3: Type: text/plain, Size: 21 bytes --]
Thanks,
Michael.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0001-Add-macros-thunk-let-and-thunk-let.patch --]
[-- Type: text/x-diff, Size: 11083 bytes --]
From 6d7028defb30b403505a72ecf68ac52fe478d562 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 `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*.
* doc/lispref/eval.texi (Deferred Eval): New section.
* doc/lispref/elisp.texi: Update menu.
---
doc/lispref/elisp.texi | 1 +
doc/lispref/eval.texi | 118 ++++++++++++++++++++++++++++++++++--
etc/NEWS | 4 ++
lisp/emacs-lisp/thunk.el | 56 +++++++++++++++++
test/lisp/emacs-lisp/thunk-tests.el | 50 +++++++++++++++
5 files changed, 224 insertions(+), 5 deletions(-)
diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi
index c752594584..a271749e04 100644
--- a/doc/lispref/elisp.texi
+++ b/doc/lispref/elisp.texi
@@ -455,6 +455,7 @@ Top
the program).
* Backquote:: Easier construction of list structure.
* Eval:: How to invoke the Lisp interpreter explicitly.
+* Deferred Eval:: Deferred and lazy evaluation of forms.
Kinds of Forms
diff --git a/doc/lispref/eval.texi b/doc/lispref/eval.texi
index 064fca22ff..8b61e39142 100644
--- a/doc/lispref/eval.texi
+++ b/doc/lispref/eval.texi
@@ -20,11 +20,12 @@ Evaluation
@ifnottex
@menu
-* Intro Eval:: Evaluation in the scheme of things.
-* Forms:: How various sorts of objects are evaluated.
-* Quoting:: Avoiding evaluation (to put constants in the program).
-* Backquote:: Easier construction of list structure.
-* Eval:: How to invoke the Lisp interpreter explicitly.
+* Intro Eval:: Evaluation in the scheme of things.
+* Forms:: How various sorts of objects are evaluated.
+* Quoting:: Avoiding evaluation (to put constants in the program).
+* Backquote:: Easier construction of list structure.
+* Eval:: How to invoke the Lisp interpreter explicitly.
+* Deferred Eval:: Deferred and lazy evaluation of forms
@end menu
@node Intro Eval
@@ -877,3 +878,110 @@ Eval
@end group
@end example
@end defvar
+
+@node Deferred Eval
+@section Deferred and Lazy Evaluation
+
+@cindex deferred evaluation
+@cindex lazy evaluation
+
+
+ Sometimes it is useful to delay the evaluation of an expression, for
+example if you want to avoid to perform a time-consuming calculation
+in the case that it turns out that the result is not needed in the
+future of the program.
+
+
+@defmac thunk-delay forms...
+Return a thunk for evaluating the @var{forms}. A thunk is a closure
+that evaluates the @var{forms} in the lexical environment present when
+@code{thunk-delay} had been called. Using this macro requires
+@code{lexical-binding}.
+@end defmac
+
+@defun thunk-force thunk
+Force @var{thunk} to perform the evaluation of the forms specified to the
+@code{thunk-delay} that created the thunk. The result of the evaluation
+of the last form is returned. The @var{thunk} also "remembers" that it has
+been forced: Any further calls of @code{thunk-force} on the same @var{thunk}
+will just return the same result without evaluating the @var{forms} again.
+@end defun
+
+@defmac lazy-let (bindings...) forms...
+This macro is analogous to @code{let} but creates "lazy" variable
+bindings. Any binding has the form (@var{symbol} @var{value-form}).
+Unlike @code{let}, the evaluation of any @var{value-form} is deferred
+until the binding of the according @var{symbol} is used for the first
+time when evaluating the @var{forms}. Any @var{value-form} is evaluated
+at most once. Using this macro requires @code{lexical-binding}.
+@end defmac
+
+@example
+@group
+(defun f (number)
+ (lazy-let ((derived-number
+ (progn (message "Calculating 1 plus 2 times %d" number)
+ (1+ (* 2 number)))))
+ (if (> number 10)
+ derived-number
+ number)))
+@end group
+
+@group
+(f 5)
+@result{} 5
+@end group
+
+@group
+(f 12)
+@print{} "Calculating 1 plus 2 times 12"
+25
+@end group
+
+@end example
+
+Because of the special nature of lazily bound variables, it is an error
+to set them (e.g.@ with @code{setq}).
+
+
+@defmac lazy-let* (bindings...) forms...
+This is like @code{lazy-let} but any expression in @var{bindings} is allowed
+to refer to preceding bindings in this @code{lazy-let*} form. Using
+this macro requires @code{lexical-binding}.
+@end defmac
+
+@example
+@group
+(lazy-let* ((x (prog2 (message "Calculating x...")
+ (+ 1 1)
+ (message "Finished calculating x")))
+ (y (prog2 (message "Calculating y...")
+ (+ x 1)
+ (message "Finished calculating y")))
+ (z (prog2 (message "Calculating z...")
+ (+ y 1)
+ (message "Finished calculating z")))
+ (a (prog2 (message "Calculating a...")
+ (+ z 1)
+ (message "Finished calculating a"))))
+ (* z x))
+
+@print{} Calculating z...
+@print{} Calculating y...
+@print{} Calculating x...
+@print{} Finished calculating x
+@print{} Finished calculating y
+@print{} Finished calculating z
+@result{} 8
+
+@end group
+@end example
+
+@code{lazy-let} and @code{lazy-let*} use thunks implicitly: their
+expansion creates helper symbols and binds them to thunks wrapping the
+binding expressions. All references to the original variables in the
+body @var{forms} are then replaced by an expression that calls
+@code{thunk-force} on the according helper variable. So, any code
+using @code{lazy-let} or @code{lazy-let*} could be rewritten to use
+thunks, but in many cases using these macros results in nicer code
+than using thunks explicitly.
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.
+
\f
* 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..8e5c8fd4d5 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))))
+
+\f
+;; 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 (macroexpand '(thunk-let ((x 1 1)) x)))
+ (should-error (macroexpand '(thunk-let (27) x)))
+ (should-error (macroexpand '(thunk-let x x))))
+
+
(provide 'thunk-tests)
;;; thunk-tests.el ends here
--
2.15.0
next prev parent reply other threads:[~2017-11-23 4:15 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
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 ` Michael Heerdegen [this message]
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
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=873755hbpr.fsf@web.de \
--to=michael_heerdegen@web.de \
--cc=eliz@gnu.org \
--cc=emacs-devel@gnu.org \
/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 external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.