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.
This commit is contained in:
Michael Heerdegen 2017-11-02 18:45:34 +01:00
parent ef183144ad
commit cc58d4de56
5 changed files with 232 additions and 5 deletions

View file

@ -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,60 @@ with the same DELAYED argument."
"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)))
;; (defalias 'lazy-let #'thunk-let)
;; (defalias 'lazy-let* #'thunk-let*)
(provide 'thunk)
;;; thunk.el ends here