From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Stefan Monnier via Users list for the GNU Emacs text editor Newsgroups: gmane.emacs.help Subject: Re: [External] : Re: function arguments optional default values Date: Sat, 06 Jul 2024 11:55:55 -0400 Message-ID: References: Reply-To: Stefan Monnier Mime-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="15972"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) To: help-gnu-emacs@gnu.org Cancel-Lock: sha1:PSvyZNqVvDYzBVwPv7x6O/UH484= Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Sat Jul 06 17:56:53 2024 Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1sQ7mP-00042n-Mv for geh-help-gnu-emacs@m.gmane-mx.org; Sat, 06 Jul 2024 17:56:53 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sQ7ll-0002GM-6N; Sat, 06 Jul 2024 11:56:13 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sQ7lj-0002Fy-VT for help-gnu-emacs@gnu.org; Sat, 06 Jul 2024 11:56:11 -0400 Original-Received: from ciao.gmane.io ([116.202.254.214]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sQ7li-0001vq-7w for help-gnu-emacs@gnu.org; Sat, 06 Jul 2024 11:56:11 -0400 Original-Received: from list by ciao.gmane.io with local (Exim 4.92) (envelope-from ) id 1sQ7le-0002w9-Rg for help-gnu-emacs@gnu.org; Sat, 06 Jul 2024 17:56:06 +0200 X-Injected-Via-Gmane: http://gmane.org/ Received-SPF: pass client-ip=116.202.254.214; envelope-from=geh-help-gnu-emacs@m.gmane-mx.org; helo=ciao.gmane.io X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.help:147087 Archived-At: >> > 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 )) the evaluation pushes the result of (or x ) 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 ))) ...), the beginning is the same, pushing the result of (or x ) 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 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 doc: ... args: (arg1) 0 constant V0 1 stack-ref 1 2 plus 3 return 7 stack-ref 2 8 call 2 9 return - Stefan