From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Mark Oteiza Newsgroups: gmane.emacs.bugs Subject: bug#28254: 26.0.50; SRFI-2 and-let* Date: Mon, 4 Sep 2017 23:55:48 -0400 Message-ID: <20170905035548.GB11331@holos.localdomain> References: <87a82kdb4e.fsf@holos> <87inh36sap.fsf@users.sourceforge.net> <20170902021043.GA7509@holos.localdomain> <878thx7qcc.fsf@users.sourceforge.net> <20170902041424.GA21189@holos.localdomain> <87tw0lzn7w.fsf@drachen> <20170902133604.GA27251@holos.localdomain> <20170904011356.GA21128@holos.localdomain> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii; format=flowed X-Trace: blaine.gmane.org 1504583959 468 195.159.176.226 (5 Sep 2017 03:59:19 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Tue, 5 Sep 2017 03:59:19 +0000 (UTC) User-Agent: Mutt/1.9.0 (2017-09-02) Cc: Michael Heerdegen , 28254@debbugs.gnu.org To: Noam Postavsky Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Tue Sep 05 05:59:04 2017 Return-path: Envelope-to: geb-bug-gnu-emacs@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 1dp50p-0007Ub-Qp for geb-bug-gnu-emacs@m.gmane.org; Tue, 05 Sep 2017 05:58:56 +0200 Original-Received: from localhost ([::1]:56719 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dp50w-0004P3-JJ for geb-bug-gnu-emacs@m.gmane.org; Mon, 04 Sep 2017 23:59:02 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:41886) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dp4yA-00026v-Q4 for bug-gnu-emacs@gnu.org; Mon, 04 Sep 2017 23:56:18 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dp4y2-0008L0-G1 for bug-gnu-emacs@gnu.org; Mon, 04 Sep 2017 23:56:10 -0400 Original-Received: from debbugs.gnu.org ([208.118.235.43]:41191) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1dp4y2-0008KE-5r for bug-gnu-emacs@gnu.org; Mon, 04 Sep 2017 23:56:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1dp4y1-0006Cb-TT for bug-gnu-emacs@gnu.org; Mon, 04 Sep 2017 23:56:01 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Mark Oteiza Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Tue, 05 Sep 2017 03:56:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 28254 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: Original-Received: via spool by 28254-submit@debbugs.gnu.org id=B28254.150458375723832 (code B ref 28254); Tue, 05 Sep 2017 03:56:01 +0000 Original-Received: (at 28254) by debbugs.gnu.org; 5 Sep 2017 03:55:57 +0000 Original-Received: from localhost ([127.0.0.1]:49872 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dp4xx-0006CJ-2z for submit@debbugs.gnu.org; Mon, 04 Sep 2017 23:55:57 -0400 Original-Received: from mail-qt0-f173.google.com ([209.85.216.173]:38073) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dp4xv-0006C4-Ma for 28254@debbugs.gnu.org; Mon, 04 Sep 2017 23:55:56 -0400 Original-Received: by mail-qt0-f173.google.com with SMTP id q8so3756478qtb.5 for <28254@debbugs.gnu.org>; Mon, 04 Sep 2017 20:55:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=udel-edu.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=fFEvg5NHsZw/CHnGDEHmvGEumspdifvC6j4qskN2W0A=; b=BnxR1+hlYhbL7I2ByyFFowLVKPCDlsqqNeO0wC+e/vqkZmELvbJDEeqt12jzYXtWNN xgvnTKC1obMBOgblK/fpCafftXzHp7uq2fI9J6Nd0pAkC3vK3PmRYI5RYfnshC08CG6B i42O4dsbir7FdxH4WI6pizUG8kAjeqFTB4I/TsiniNG7JkK2GDiDGJYMS91PxjZy/q+T Q+Ytaga96cx4vyl6rXcWz5OoUjvxHai8h3Gh0snWbOfwaKR/TOJEXLArbdPv39SpNaQG WVPdZ96qSTJ/dlPJiwwSHua2TIale9KeVUpIYZj6L/zo0I0m35f9weEDbgntSWFX7xUo idDQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=fFEvg5NHsZw/CHnGDEHmvGEumspdifvC6j4qskN2W0A=; b=XNRPFjZmVMTRHbgayDw5SmXEtkf1UdiDMT5JcMu5dkFRI5sa/MombfzVUUgIhj9mBs MPNo14gTcwWclMCFbaSwFFVAuAgrP+iHxuHHBWvu+iWkN0VMgQcHzBnJiDsh0N8oxpHT 2et1y/JPLw6ytdiQPS+3X2Jw9MzgGU1YkT3K0GPI+D2Tbiqiypjzbc5UQKoxmYkzZX5j JTe4l7X041CDgAPp33dC4m2pvzxs4U880Zsb3ApVAC2+Ti+s2PEGtPeDSVHx2F5ZuG9J sdMqCw/HJPnwYZfUnlw+Lck04be7SycVkXG/8MJJUmWFFLypYCdY/osLGMuY+Sk69gSb 5oTw== X-Gm-Message-State: AHPjjUj74X7kZMV1wYSyOX9+5kwkq3/Njdy+C8Q2qr7ReYBnrmIVN5/V Fpd8j3G1gTEsb0rnZ95RVw== X-Google-Smtp-Source: ADKCNb7G208HT7ZhJ3f6EvMmXQ9t8czIlgibAffQ6iCkM7mP35G0j4DqAKutm20OFPT9WKKt/zvfvQ== X-Received: by 10.200.39.188 with SMTP id w57mr3548456qtw.285.1504583749868; Mon, 04 Sep 2017 20:55:49 -0700 (PDT) Original-Received: from holos.localdomain (pool-173-64-88-95.bltmmd.fios.verizon.net. [173.64.88.95]) by smtp.gmail.com with ESMTPSA id z187sm4721533qke.4.2017.09.04.20.55.49 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Mon, 04 Sep 2017 20:55:49 -0700 (PDT) Original-Received: by holos.localdomain (Postfix, from userid 1000) id CBE906BF86; Mon, 4 Sep 2017 23:55:48 -0400 (EDT) Content-Disposition: inline In-Reply-To: <20170904011356.GA21128@holos.localdomain> X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 208.118.235.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.org gmane.emacs.bugs:136592 Archived-At: On 03/09/17 at 09:13pm, Mark Oteiza wrote: >On 02/09/17 at 02:41pm, Noam Postavsky wrote: >>On Sat, Sep 2, 2017 at 9:36 AM, Mark Oteiza wrote: >>>This single tuple special case is troublesome IMO: >>> >>> (if-let* (x) "dogs" "cats") => "cats" >>> (if-let* (x (y 2)) "dogs" "cats") => (void-function y) >>> (if-let* (x (y 1) (z 2)) "dogs" "cats") => "cats" >>> >>>I'm curious if this was brought up in the old discussion when this was >>>implemented. > >FWIW, this was brought up in the original thread. >https://lists.gnu.org/archive/html/emacs-devel/2014-08/msg00228.html > >IMO the original suggestion of having if-let and when-let be exclusively >single binding, while the starred versions excluding the single binding >special case would be more sane. > >P.S. I just realized I didn't copy the tuple part of if-let* into >and-let* in the patch I just sent, and therefore missed the problem this >special case causes in tests. This is a patch implementing the above: if-let and when-let only take single tuple, while {if,when,and}-let* lose the single tuple special case. diff --git a/etc/NEWS b/etc/NEWS index 2b0c86d7af..24568d637b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1120,6 +1120,14 @@ be disabled by setting 'byte-compile-cond-use-jump-table' to nil. --- ** The alist 'ucs-names' is now a hash table. +--- +** Semantics of 'if-let', 'when-let', 'if-let*', and 'when-let*' have +changed. 'if-let' and 'when-let' now only accept a single tuple to +bind a single symbol. 'if-let*' and 'when-let*' no longer accept the +single tuple special case. New macro 'and-let*' is an implementation +of the Scheme SRFI-2 syntax of the same name. 'if-let*' and +'when-let*' now accept the same binding syntax as 'and-let*'. + --- ** 'C-up', 'C-down', 'C-left' and 'C-right' are now defined in term mode to send the same escape sequences that xterm does. This makes @@ -1479,10 +1487,6 @@ It avoids unnecessary consing (and garbage collection). +++ ** 'car' and 'cdr' compositions 'cXXXr' and 'cXXXXr' are now part of Elisp. ---- -** 'if-let*', 'when-let*', and 'and-let*' are new in subr-x.el. -The incumbent 'if-let' and 'when-let' are now aliases. - --- ** Low-level list functions like 'length' and 'member' now do a better job of signaling list cycles instead of looping indefinitely. diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index 849ac19d6a..e59211a1dc 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -83,10 +83,15 @@ thread-last `(internal--thread-argument nil ,@forms)) (defsubst internal--listify (elt) - "Wrap ELT in a list if it is not one." - (if (not (listp elt)) - (list elt) - elt)) + "Wrap ELT in a list if it is not one. +If ELT is of the form ((EXPR)), listify (EXPR) with a dummy symbol." + (cond + ((symbolp elt) (list elt elt)) + ((and (null (cdr elt)) + (let ((form (car elt))) + (or (listp form) (atom form)))) + (list (cl-gensym) (car elt))) + (t elt))) (defsubst internal--check-binding (binding) "Check BINDING is properly formed." @@ -98,7 +103,10 @@ internal--check-binding (defsubst internal--build-binding-value-form (binding prev-var) "Build the conditional value form for BINDING using PREV-VAR." - `(,(car binding) (and ,prev-var ,(cadr binding)))) + (let ((var (car binding))) + (if (and (null (cdr binding)) (atom (car binding)) (not (symbolp (car binding)))) + `(,var (and ,prev-var ,var)) + `(,var (and ,prev-var ,(cadr binding)))))) (defun internal--build-binding (binding prev-var) "Check and build a single BINDING with PREV-VAR." @@ -117,44 +125,62 @@ internal--build-bindings binding)) bindings))) -(defmacro if-let* (bindings then &rest else) +(defmacro if-let* (varlist then &rest else) "Bind variables according to VARLIST and eval THEN or ELSE. Each binding is evaluated in turn with `let*', and evaluation stops if a binding value is nil. If all are non-nil, the value of THEN is returned, or the last form in ELSE is returned. + Each element of VARLIST is a symbol (which is bound to nil) -or a list (SYMBOL VALUEFORM) (which binds SYMBOL to the value of VALUEFORM). -In the special case you only want to bind a single value, -VARLIST can just be a plain tuple. -\n(fn VARLIST THEN ELSE...)" +or a list (SYMBOL VALUEFORM) (which binds SYMBOL to the value +of VALUEFORM). +An element can additionally be of the form (EXPR), which is +evaluated and checked for nil." (declare (indent 2) - (debug ([&or (&rest [&or symbolp (symbolp form)]) (symbolp form)] + (debug ((&rest [&or symbolp (symbolp form) (sexp)]) form body))) - (when (and (<= (length bindings) 2) - (not (listp (car bindings)))) - ;; Adjust the single binding case - (setq bindings (list bindings))) - `(let* ,(internal--build-bindings bindings) - (if ,(car (internal--listify (car (last bindings)))) - ,then - ,@else))) + (if varlist + `(let* ,(setq varlist (internal--build-bindings varlist)) + (if ,(caar (last varlist)) + ,then + ,@else)) + `(let* () ,@else))) -(defmacro when-let* (bindings &rest body) +(defmacro when-let* (varlist &rest body) "Bind variables according to VARLIST and conditionally eval BODY. Each binding is evaluated in turn with `let*', and evaluation stops if a binding value is nil. If all are non-nil, the value of the last form in BODY is returned. -Each element of VARLIST is a symbol (which is bound to nil) -or a list (SYMBOL VALUEFORM) (which binds SYMBOL to the value of VALUEFORM). -In the special case you only want to bind a single value, -VARLIST can just be a plain tuple. -\n(fn VARLIST BODY...)" + +VARLIST is the same as in `if-let*'." + (declare (indent 1) (debug if-let*)) + (list 'if-let* varlist (macroexp-progn body))) + +(defmacro and-let* (varlist &rest body) + "Bind variables according to VARLIST and conditionally eval BODY. +Like `when-let*', except if BODY is empty and all the bindings +are non-nil, then the result is non-nil." + (declare (indent 1) (debug when-let*)) + (let (res) + (if varlist + `(let* ,(setq varlist (internal--build-bindings varlist)) + (if ,(setq res (caar (last varlist))) + ,@(or body `(,res)))) + `(let* () ,@(or body '(t)))))) + +(defmacro if-let (spec then &rest else) + "Bind variables according to SPEC and eval THEN or ELSE. +Like `if-let*' except SPEC is of the form (SYMBOL VALUEFORM)" + (declare (indent 2) (debug ((symbolp form) form body))) + (if spec + `(let (,spec) (if ,(car spec) ,then ,@else)) + `(let () ,@else))) + +(defmacro when-let (spec &rest body) + "Bind variables according to SPEC and conditionally eval BODY. +Like `when-let*' except SPEC is of the form (SYMBOL VALUEFORM)" (declare (indent 1) (debug if-let)) - (list 'if-let bindings (macroexp-progn body))) - -(defalias 'if-let 'if-let*) -(defalias 'when-let 'when-let*) -(defalias 'and-let* 'when-let*) + (list 'if-let spec (macroexp-progn body))) (defsubst hash-table-empty-p (hash-table) "Check whether HASH-TABLE is empty (has 0 elements)."