help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Understanding the "let" construct and the setting of variables


From: Joost Kremers
Subject: Re: Understanding the "let" construct and the setting of variables
Date: Thu, 17 Dec 2020 01:21:56 +0100
User-agent: mu4e 1.5.7; emacs 27.1.50

On Thu, Dec 17 2020, steve-humphreys@gmx.com wrote:
> I have been writing some elisp to set the time grid in the agenda.
> The discussion progressed towards the use of the "let" construct.
>
> But, the discussion got too advanced for me to follow the different
> points of view and make a decision.
>
> This message is for showing some examples, of how to set and use variables
> in a "let", because people criticise using "setq".  But discussion needs
> simple examples  that would not overwhelm a relative beginner.
>
> (defun timfutur ()
>    (interactive)
>    (setq tim 845)
>    (setq tsk 80)
>
>    (setq thr (/ tim 100))
>    (setq tmn (- tim (* thr 100)))
>
>    (setq tinc_mn (+ tmn tsk))
>    (setq tinc_hr (/ (+ tmn tsk) 60))
>    (setq tinc_mn (- tinc_mn (* tinc_hr 60)) )
>
>    (setq thr_futur (* (+ thr tinc_hr) 100)  )
>    (setq tmn_futur tinc_mn)
>    (setq tim_out (+ thr_futur tmn_futur))

I'm not sure what exactly you're asking, (I'm wondering if your message is
complete or was accidentally sent before you finished it), but to understand the
problem with `setq`, evaluate your function above in the `*scratch*` buffer.
(Copy the function into the `*scratch*` buffer, put the cursor right after it
and press `C-x C-e`; note that you need to add another closing parenthesis on
the last line). That will define your function and make it available to
Emacs.

Then open a Lisp interaction buffer with `M-x ielm RET`. You'll get a buffer
called `*ielm*` with a prompt where you can type Elisp expressions that get
executed right away. Type `tim` (without parentheses) and hit RET. You should
get a void variable error:

    *** Eval error ***  Symbol’s value as variable is void: tim"

Then type `(timfutur)` and hit RET. You'll get the return value 1005 (displayed
also in octal and hexadecimal).

Now type `tim` again at the prompt. This time, there won't be an error anymore.
Instead you'll get the value 845.

Running your function has created a global variable `tim` (plus all the other
variables you've setq'ed), and since there are no packages or namespaces in
Elisp, your variable is now available to all of Emacs.

While I was writing this, your two questions arrived:

> 1. In what simple circumstances would one use a "setq" in the body of a let?

One common idiom would be to create or consume a list inside a loop, e.g., 

```
(let ((lst (some-function-that-produces-a-list)))
  (while (some-condition-on (car lst))
    (do-something-with (car lst))
    (setq lst (cdr lst))))
```

Nowadays such an idiom would more often be handled with a higher-order function
of the map/reduce/filter-family, but there may be situations in which that
doesn't work.

Another example would be the case where you want to modify a value based on some
set of conditions, e.g.,:

```
(let ((x (get-some-value))
      (y (get-some-other-value)))
  (cond
   ((some-condition-on y)
    (setq x (do-something-with y)))
   ((some-other-condition-on y)
    (setq x (do-something-else-with x)))
   (:otherwise
    (setq y nil)))
  (now-do-something-with x y))
```

You could probably rewrite this without `setq` using `let*` and some
intermediate variables, but sometimes I find using `setq` to be clearer,
especially if you have multiple values that are interdependent in complex ways.

> 2. What simple option does one have that is more advantageous than using a 
> "setq"?

`let*` in the example function you gave above. For creating or consuming a list,
there's the map/filter/reduce-family or cl-loop.

Not sure if that makes it any easier for a relative beginner :-/ but I hope it
helps a bit anyway.


-- 
Joost Kremers
Life has its moments



reply via email to

[Prev in Thread] Current Thread [Next in Thread]