From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Newsgroups: gmane.emacs.help Subject: let, let*, oh, why [was: Elisp - Function returning a list] Date: Wed, 16 Dec 2020 12:52:42 +0100 Message-ID: <20201216115242.GB11162@tuxteam.de> References: <87k0ti9ygy.fsf@zoho.eu> <87czza8g37.fsf@zoho.eu> Mime-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="Y7xTucakfITjPcLV" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="27074"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Mutt/1.5.21 (2010-09-15) To: help-gnu-emacs@gnu.org Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Wed Dec 16 12:53:33 2020 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 1kpVN7-0006w3-ET for geh-help-gnu-emacs@m.gmane-mx.org; Wed, 16 Dec 2020 12:53:33 +0100 Original-Received: from localhost ([::1]:46420 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kpVN6-0000ID-Gb for geh-help-gnu-emacs@m.gmane-mx.org; Wed, 16 Dec 2020 06:53:32 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:39902) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kpVMT-0000Hw-NR for help-gnu-emacs@gnu.org; Wed, 16 Dec 2020 06:52:53 -0500 Original-Received: from mail.tuxteam.de ([5.199.139.25]:60864) by eggs.gnu.org with esmtps (TLS1.2:DHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.90_1) (envelope-from ) id 1kpVMR-0001Vp-7V for help-gnu-emacs@gnu.org; Wed, 16 Dec 2020 06:52:53 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=tuxteam.de; s=mail; h=From:In-Reply-To:Content-Type:MIME-Version:References:Message-ID:Subject:To:Date; bh=/pbC+trzu48bc0UcOiREYaGf0VyX4ajvHi5TfIDQOpo=; b=UztUm1oUGzKS0f8GlQqJkxtlTpR3LVILSmnS4pMNPwVt5W4MkaD97wnSusEnaPXMT991Gm3VEsZYMpybUW3+QXlpOVcEOqicATx9EDfnm5jAoAI+Z6lU2FyP572jBfefg40/z2NvkZ58y3afK2DxL4EsD2Deuc9amICOFYY7N+OyshWmjsXJm2hhYmYrIt237jFvw9LxnTnRDWZi8zErkl1O4Kc8WIecgwjRlF4z2Lq2/9GZ+zWQ0z59Rne+pBvXgUpafkB8VCkO6PVKYL3QbXrPO4255Gq6YnEC9qJuCIPD2Gk6x5XMRSm60LlXehVObjj7x6BplawpdTCODyDgpA==; Original-Received: from tomas by mail.tuxteam.de with local (Exim 4.80) (envelope-from ) id 1kpVMI-0004hc-Le for help-gnu-emacs@gnu.org; Wed, 16 Dec 2020 12:52:42 +0100 Content-Disposition: inline In-Reply-To: Received-SPF: pass client-ip=5.199.139.25; envelope-from=tomas@tuxteam.de; helo=mail.tuxteam.de X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, 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.23 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" Xref: news.gmane.io gmane.emacs.help:126418 Archived-At: --Y7xTucakfITjPcLV Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Wed, Dec 16, 2020 at 08:36:03AM +0100, steve-humphreys@gmx.com wrote: > > From: "Emanuel Berg via Users list for the GNU Emacs text editor" [...] > > It is better to do all computation in the `let's, then use > > them. No `setq' needed. I generally agree, but not 100% (all generalizations suck, you know ;-) > Example? Is there anything written about it? What setq does is to "search upwards" from where it is until it "finds the variable", and then sets its value as instructed. If it doesn't find that variable, it creates a new one at the "top level", to be able to accomplish this task. I put "search upwards" and "find the variable" in quotes, because they deserve some explanation. I'll limit myself here to the "upwards" part: What "upwards" means depends on whether you are under dynamic scope (traditional) or lexical scope (more modern, recommended almost always). Under dynamic scope, the search for the variable goes up the call chain: if the function where the setq is in "sees" that variable, then it's there. Otherwise it asks its caller, and so on. Under lexical scope, the search considers the source code: if that variable is visible/defined in the current expression (think "block", if you're coming from C/Perl/Python/PHP/Java), then that's it. Otherwise go look in the enclosing expression. Needless to say, this is the source of lots of fun: if you are doing something in your code and import a snippet of code from elsewhere (for the lexical case) or just call some code elsewhere, and "they" trample on your "variables", spooky things happen. In the dynamic case, those things are very spooky. Imagine you have some code: (setq x 3) (setq y 4) and call on Tomas's library to get the last mouse click coordinates. (setq mouse-x (car (tomas-coords))) Jane's library does this (CAVEAT: there are nicer ways to do this, apart from the "obvious" error that this function tramples over whatever values mouse-pos, x, and y might have "globally". I'm doing that to have a small working example. I repeat: DON'T DO IT THIS WAY ;-) (defun tomas-coords () (setq mouse-pos (mouse-position)) ; elisp manual 29.16 ;; mouse-position has as first arg the frame. Let's get rid of that: (setq x (cadr mouse-pos)) (setq y (cddr mouse-pos)) (cons x y)) ;; return the pair (x . y) Now your 'x' above isn't 3 anymore, but has some random value depending on where your user was wiggling around the mouse. Oops. Now under lexical scope things aren't so bleak, but still annoying. That's why function arguments and let are there. They create a kind of barrier for private variable names. A slightly better version of the above function would then be: (defun tomas-coords () (let ((mouse-pos (mouse-position))) ; elisp manual 29.16 ;; mouse-position has as first arg the frame. Let's get rid of that: (let ((x (cadr mouse-pos)) (y (cddr mouse-pos))) (cons x y)))) ;; return the pair (x . y) =20 So the `let' is telling elisp: "look, we are introducing here a new variable called `x'. It has nothing to do with any other `x' out/up there". Or "Any Similarity to Persons Living or Dead is Purely Coincidental", as some like to put it. Still, the "here" in the phrase above is interpreted differently depending on whether we're "flying" dynamically or lexically. Why the two nested `let's above, you ask? Well, in the variable- assignment part of a let each arm is independent of the others. The line above ...(y (cddr mouse-pos)) doesn't "see" the `x' defined above it. It is as if all those parts were happening at the same time. So for the `x' and `y' assignments to "see" the just assigned `mouse-pos', we must `let' that happen before. That's why `let*' has been invented. Basically, (let* ((foo blah) (bar bleh) (baz mih)) ...) is equivalent, but arguably more readable than (let ((foo blah)) (let ((bar bleh)) (let ((baz mih)) ...))) HTH - t --Y7xTucakfITjPcLV Content-Type: application/pgp-signature; name="signature.asc" Content-Description: Digital signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iEYEARECAAYFAl/Z9QoACgkQBcgs9XrR2kZ+PwCffiftCBhO0CEA0wlYV/GQWKft 3UkAoIDeMKaKbpt3rPy7hBUWhi6Flsci =xtVX -----END PGP SIGNATURE----- --Y7xTucakfITjPcLV--