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: `thunk-let'? Date: Thu, 09 Nov 2017 19:39:55 +0100 Message-ID: <87mv3vwb2c.fsf_-_@web.de> References: <87infp9z6j.fsf@web.de> <87zi90eehg.fsf@web.de> <87o9ocd6s4.fsf@web.de> <87wp2zcwm2.fsf@web.de> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1510252853 16070 195.159.176.226 (9 Nov 2017 18:40:53 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Thu, 9 Nov 2017 18:40:53 +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 Thu Nov 09 19:40:49 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 1eCrkp-0003lj-EZ for ged-emacs-devel@m.gmane.org; Thu, 09 Nov 2017 19:40:43 +0100 Original-Received: from localhost ([::1]:38326 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eCrkw-0000Yx-MC for ged-emacs-devel@m.gmane.org; Thu, 09 Nov 2017 13:40:50 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:38205) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eCrkH-0000Xb-7X for emacs-devel@gnu.org; Thu, 09 Nov 2017 13:40:10 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eCrkC-00022r-A2 for emacs-devel@gnu.org; Thu, 09 Nov 2017 13:40:09 -0500 Original-Received: from mout.web.de ([212.227.15.14]:57285) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1eCrkB-000227-U1 for emacs-devel@gnu.org; Thu, 09 Nov 2017 13:40:04 -0500 Original-Received: from drachen.dragon ([94.217.120.125]) by smtp.web.de (mrweb004 [213.165.67.108]) with ESMTPSA (Nemesis) id 0MABh3-1eO7Cn10HJ-00BJVT; Thu, 09 Nov 2017 19:39:56 +0100 In-Reply-To: <87wp2zcwm2.fsf@web.de> (Michael Heerdegen's message of "Thu, 09 Nov 2017 16:14:45 +0100") X-Provags-ID: V03:K0:D/9C0ci/bB5NjkbxehUdY24pNMaKkFEuKL5TopRYj40Gpefs0wM F3mMMXpBeIrNouR+F+mV632wUN8mFkEhEO6z9P3wpKvfz6u1ElqYsWTGLGUjt6c3KGrYaWL jzUaasgPir41qqHU18sqjramQk+VJX/Dsuc0Ud9q2WlvALL0+jRQWnhqpskCMxwTgYy9ZLB RiOhv7PnlNU4mwHiEGyOg== X-UI-Out-Filterresults: notjunk:1;V01:K0:qwmF7StmAWY=:K8Ye3KvRJutshXcPXSKrrQ KNxNnwoT1TTquhrwWT074BJBsqPPlKyP0jfwry6Etd1RMgBZe/kenvZ3ZYUcJbOEV7Q4Y4Knm GIz9ppCbkSWX09oEr79PwdMJv5OIzQ3GrzoCZwFmp/+W/0kN+VklY+te1h5eHvfpal+lcypDM 3RmfqBnRiW97gXHvhhDJ7KVcYR9641JeLk9yAS8axd+piemsItDI8L0LOdwVj6NaD0q94nPSP Vn8g9gT254nmVMI6ocpR4QMY4RzQzPl9OKMP59jwe20+3dE/wxqwJZsmney3dOCWoYFpgV644 APjrNPZvKzO+BQMKHA84plmQvfOGmkBbT0Wnpi0h4gfEX0htRGcH+KEOgwZ0blyOhxEMUgIFl jo4ChDl1f2+q0DSjkxM3uX+cCGTXOu5zptXJ6aAhGO8wwxaZxok16FLjuyHd31MVE+n/5Orx7 QtWfggerTrokAUgczMUiSwqpmqYkblOCjJ24cwbbNT2xqzVVQL/49qJGDP61H7zpg8iClNQdk 4wBa4oWv4Cn115fUnponeFTmE/HblBOD4GxhotCdN/CQf5JI2n4k9ldnKY7OP+G4DU1iZu81U Yj6DnS6l88OmXr+y8o7JK+hyK/kjQqNj9AO5WJScVkMcD0E59k1vTCIZ3yXhIma9sAqIMPxPd 1MeviPpcRdGVWG8GW01MNS4x21/wivXoR1zPJECa/vzNWONdShL3vyaKm++V9sWwixjfUjKz6 UXww/w24JVaSJ/PTJ8ec19Z9njgRq1Ag/vy+4L19CX2hyiL6zjResdiuiwWt2+p+K1lKolA0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 212.227.15.14 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:220004 Archived-At: --=-=-= Content-Type: text/plain Michael Heerdegen writes: > > Maybe a better option is to add a `require` in the expansion of > > lazy-let? But then we need to wrap it in `eval-and-compile' because we want to expand the (macro) `thunk-delay' that appears in the expansion at compile time. Attached is the cumulative patch doing that. --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Add-macros-lazy-let-and-lazy-let.patch >From ca7f5a62e2bda9ee5e8d9199ae80714674e18511 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. * 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. * 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..ea22bee13f 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 + (eval-and-compile (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"))) + +;; 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. --=-=-=--