unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
From: Stefan Monnier via Users list for the GNU Emacs text editor <help-gnu-emacs@gnu.org>
To: help-gnu-emacs@gnu.org
Subject: Re: [External] : Re: function arguments optional default values
Date: Sat, 06 Jul 2024 11:55:55 -0400	[thread overview]
Message-ID: <jwvjzhya6je.fsf-monnier+emacs@gnu.org> (raw)
In-Reply-To: SJ0PR10MB54887FCD2206E402BF2FD8FFF3D82@SJ0PR10MB5488.namprd10.prod.outlook.com

>> > Does elisp support function arguments optional default values ?
>> I solve that problem with following:
>> (defun my-function (arg &optional other-arg)
>>   (let* ((other-arg (or other-arg my-default)))
>>     other-arg))
> Simpler - no reason for another binding,
> just change the value (it's a local var).
>
> (defun my-function (arg &optional other-arg)
>   (setq other-arg  (or other-arg my-default))
>   ...)

FWIW, assuming you're using `lexical-binding` (which you should), this
tends to result in less efficient byte-code.

The difference is marginal in most cases (and efficiency of ELisp code
is irrelevant in most cases as well), but the point is that bindings
don't "cost" as much as mutation.

Here is how this usually plays out: with (setq x (or x <default>)) the
evaluation pushes the result of (or x <default>) on the stack, does the
`setq` which consumes the value on the stack and modifies the value of
`x`, which itself lives on the stack.

With (let ((x1 (or x <default>))) ...), the beginning is the same,
pushing the result of (or x <default>) on the stack, but then the
execution of the `let` is the "free" act of keeping that value on the
stack (i.e. doing nothing), and instead of referring to the stack
element that holds `x`, we will simply refer to the stack element that
holds `x1`.
[ There is still a cost to `let` in the fact that the stack grows a bit
  more, of course, but it's usually of no consequence.  ]

Compare

    (let ((lexical-binding t))
      (disassemble '(lambda (y &optional x)
                      (setq x (or x 5)) (+ y x))))
=>
    byte code:
      doc:   ...
      args: (arg1 &optional arg2)
    0       dup
    1       goto-if-not-nil-else-pop 1
    4       constant  5
    5:1     stack-set 1
    7       stack-ref 1
    8       stack-ref 1
    9       plus
    10      return

with

    (let ((lexical-binding t))
      (disassemble '(lambda (y &optional x)
                      (let ((x1 (or x 5))) (+ y x1)))))
=>
    byte code:
      doc:   ...
      args: (arg1 &optional arg2)
    0       dup
    1       goto-if-not-nil-else-pop 1
    4       constant  5
    5:1     stack-ref 2
    6       stack-ref 1
    7       plus
    8       return

The difference is more drastic when the variable is captured by
a closure because our closure conversion is too naïve to understand that
the `setq` happens "once and forall" before we capture the variable, so
it presumes that the var may still be mutated after the closure is
constructed, which means that the closure can't just hold a copy of the
var's value but really needs to hold a "reference to the variable",
which we do by storing the variable's value inside a cons-cell:

Compare

    (let ((lexical-binding t))
      (disassemble '(lambda (&optional x)
                      (setq x (or x 5)) (lambda (y) (+ x y))))):
=>
    byte code:
      doc:   ...
      args: (&optional arg1)
    0       dup
    1       list1
    2       dup
    3       stack-ref 1
    4       car-safe
    5       goto-if-not-nil-else-pop 1
    8       constant  5
    9:1     setcar
    10      discard
    11      constant  make-closure
    12      constant  <byte-code-function>
          doc:   ...
          args: (arg1)
        0       constant  V0
        1       car-safe
        2       stack-ref 1
        3       plus
        4       return

    13      stack-ref 2
    14      call      2
    15      return

with

    (let ((lexical-binding t))
      (disassemble '(lambda (&optional x)
                      (let ((x1 (or x 5))) (lambda (y) (+ x1 y))))))
=>
    byte code:
      doc:   ...
      args: (&optional arg1)
    0       dup
    1       goto-if-not-nil-else-pop 1
    4       constant  5
    5:1     constant  make-closure
    6       constant  <byte-code-function>
          doc:   ...
          args: (arg1)
        0       constant  V0
        1       stack-ref 1
        2       plus
        3       return

    7       stack-ref 2
    8       call      2
    9       return


- Stefan




  reply	other threads:[~2024-07-06 15:55 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-07-03 21:21 function arguments optional default values Heime
2024-07-03 21:51 ` Stephen Berman
2024-07-03 21:56   ` Heime
2024-07-03 22:04     ` Stephen Berman
2024-07-03 22:24       ` Stephen Berman
2024-07-06 12:43 ` Jean Louis
2024-07-06 15:03   ` [External] : " Drew Adams
2024-07-06 15:55     ` Stefan Monnier via Users list for the GNU Emacs text editor [this message]
2024-07-06 16:06       ` Drew Adams
2024-07-06 19:04         ` Stefan Monnier via Users list for the GNU Emacs text editor
2024-07-06 21:48       ` Heime

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