unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Stefan Monnier <monnier@iro.umontreal.ca>
To: emacs-devel@gnu.org
Subject: named-let
Date: Fri, 08 Jan 2021 20:43:13 -0500	[thread overview]
Message-ID: <jwvpn2egaj3.fsf-monnier+emacs@gnu.org> (raw)

The recent discussion around loops motivated me to look a bit further
into named-let.  It's actually easy to define:

    (defmacro named-let (name bindings &rest body)
      "Looping construct taken from Scheme.
    Like `let', bind variables in BINDINGS and then evaluate BODY,
    but with the twist that BODY can evaluate itself recursively by
    calling NAME, where the arguments passed to NAME are used
    as the new values of the bound variables in the recursive invocation."
      (declare (indent 2) (debug (symbolp (&rest (symbolp form)) body)))
      (let ((fargs (mapcar (lambda (b) (if (consp b) (car b) b)) bindings))
            (aargs (mapcar (lambda (b) (if (consp b) (cadr b))) bindings)))
        ;; According to the Scheme semantics of named let, `name' is not in scope
        ;; while evaluating the expressions in `bindings', and for this reason, the
        ;; "initial" function call below needs to be outside of the `cl-labels'.
        `(funcall
          (cl-labels ((,name ,fargs . ,body)) #',name)
          . ,aargs)))

You can then define

    (defun my-length (xs)
      (named-let loop ((xs xs) (n 0))
        (if xs
            (loop (cdr xs) (1+ n))
          n)))

Now this definition of length is recursive, so without some proper tail
call optimization, it'll burp on any longish list (apparently 1000
elements are sufficient).

But with the recent tail-call optimization I installed into `master`,
the above `my-length` now works without eating up stack space.
It's still not as efficient as a hand-written `while` loop, but it's not
that bad.  Here's the byte-code for the above function:

    byte code:
      doc:   ...
      args: (arg1)
    0       dup       
    1       constant  0
    2       constant  nil
    3:1     stack-ref 2
    4       stack-ref 2
    5       stack-ref 1
    6       goto-if-nil 2
    9       stack-ref 1
    10      cdr       
    11      stack-set 5
    13      dup       
    14      add1      
    15      stack-set 4
    17      constant  :recurse
    18      goto      3
    21:2    dup       
    22      stack-set 3
    24      constant  nil
    25:3    discardN-preserve-tos 2
    27      goto-if-not-nil 1
    30      dup       
    31      stack-set 1
    33      return    

Notice how there's really no `lambda` nor `funcall` that remains.


        Stefan




             reply	other threads:[~2021-01-09  1:43 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-09  1:43 Stefan Monnier [this message]
2021-01-09  8:58 ` named-let tomas
2021-01-09 16:01   ` named-let Joost Kremers
2021-01-09 21:48     ` named-let Stefan Monnier
2021-01-09 15:23 ` named-let Tomas Hlavaty
2021-01-09 16:44   ` named-let Stefan Monnier
2021-01-09 17:03     ` named-let Helmut Eller
2021-01-09 18:43       ` named-let Stefan Monnier
2021-01-09 18:47     ` named-let Tomas Hlavaty
2021-01-10 18:49   ` named-let Zhu Zihao
2021-01-11 22:27     ` named-let Tomas Hlavaty
2021-01-11 22:36       ` named-let Stefan Monnier
2021-01-11 22:48         ` named-let Tomas Hlavaty
2021-01-11 22:50         ` named-let Andrea Corallo via Emacs development discussions.
2021-01-11 23:10           ` named-let Stefan Monnier
2021-01-11 23:28             ` named-let Andrea Corallo via Emacs development discussions.
2021-01-11 23:57               ` named-let Stefan Monnier
2021-01-12  9:24                 ` named-let Andrea Corallo via Emacs development discussions.
2021-01-12 18:07                   ` named-let Stefan Monnier
2021-01-12 18:50                     ` named-let Andrea Corallo via Emacs development discussions.
2021-01-12  9:13             ` named-let Helmut Eller
2021-01-13  8:11         ` named-let Zhu Zihao
2021-01-13 14:01           ` named-let Stefan Monnier
2021-01-11 22:40       ` named-let Andrea Corallo via Emacs development discussions.
2021-01-11 22:51         ` named-let Tomas Hlavaty
2021-01-11 23:04           ` named-let Andrea Corallo via Emacs development discussions.
2021-01-12  0:12             ` named-let Tomas Hlavaty
2021-01-20 19:44 ` named-let Stefan Monnier

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=jwvpn2egaj3.fsf-monnier+emacs@gnu.org \
    --to=monnier@iro.umontreal.ca \
    --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 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).