reddit / emacs
Changing the value of a string variable after a let statement
owmagow · 2022-09-06 15:13
I’m not a professional programmer … just trying to learn more about global vs. local variables.
I’ve done quite a bit of researching this issue, but it is just a tad over my head at the moment …
How do I change the value of a string variable within a let statement without resorting to using setq?
For example …
(let
((name “Sam”))
(… something here to make
name = “Bill”)
)
lebensterben · 2022-09-06 15:24
use another `let`....
owmagow · 2022-09-06 15:26
And that would be visible from the body of the first let?
EternalMigrainity · 2022-09-06 15:27
This might not be the comment you want but you could do (set 'name "Obadiah"), kinda cheating but yeah
lebensterben · 2022-09-06 15:27
of course not. that's just a rebinding of the symbol, which is only in effect within the body of the second let.
owmagow · 2022-09-06 15:29
I’m see. That seems to limit what you can do within the body of the first let.
Is there no way to rebind it within the body of the first let?
Pretty much every other language I’ve played with let’s you change the value of local variables within their local range. Does Elisp really not allow that?
lebensterben · 2022-09-06 15:32
Why do you refuse to use setq?
__deetz__ · 2022-09-06 15:33
I don't know about re-binding, but I can see two options here:
- do whatever you want to be done after the second let within it.
- instead refactor what the second let does into a function that you then call to bind the value to the eventual name in the first place.
Maybe sharing some more details would help to come up with a proper (meaning: not global namespace polluting) solution.
owmagow · 2022-09-06 15:34
I actually DID end up using setq. I’m just trying to figure out if there is a more correct way of doing it.
owmagow · 2022-09-06 15:38
That second bullet point is definitely doable. Thank you.
Still, though it is weird that Elisp doesn’t seem to have an easy mechanism for this.
I wonder if Common Lisp accounts for this.
owmagow · 2022-09-06 15:40
… wouldn’t that make it global?
(I ended up using setq, so I’m not opposed to doing that. )
EternalMigrainity · 2022-09-06 15:41
No it doesn't make it global
owmagow · 2022-09-06 15:43
Ah, okay! Thanks, I’ll research set more. Pretty new to elisp, but figuring it out. Appreciate your help.
__deetz__ · 2022-09-06 15:50
While I am a professional programmer, Lisp is only something I only do occasionally (actually today :D ) for emacs, and I'm aware that it's special... but from my experience with functional languages like clojure (lisp-like, very much so), and a few ML-style languages, I'd argue: variables are often meant to be single assignment only. You solve re-binding then with recursion for example. Now there are obvious exceptions to this, a dotimes for example re-binds the index variable. But I also rarely re-bind in my scripting. Just look at other code and see how people are doing things. That's what I have to do given my relative little exposure to elisp anyway :D
00-11 · 2022-09-06 16:18
(let ((name “Sam”)) (… (setq name “Bill”)))
Illiamen · 2022-09-06 16:22
This behavior is mentioned on this page: https://www.gnu.org/software/emacs/manual/html_node/elisp/Local-Variables.html
> To take another example, the `let` special form explicitly establishes local bindings for specific variables, which take effect only within the body of the `let` form.
>
> ...
>
> Establishing a local binding saves away the variable’s previous value (or lack of one). We say that the previous value is shadowed. Both global and local values may be shadowed. If a local binding is in effect, using `setq` on the local variable stores the specified value in the local binding. When that local binding is no longer in effect, the previously shadowed value (or lack of one) comes back.
beriset · 2022-09-06 16:38
Without seeing your code I can’t be sure, but are you sure you want to change the variable value? Functional languages (let’s include lisps here) generally discourage that and offer better alternatives: rebinding, recursion, looping structures.
owmagow · 2022-09-06 17:00
Ah! Now that makes a lot of sense!
So, essentially, setq stays local.
Thank you!
owmagow · 2022-09-06 17:02
I see what you are saying about their being various ways to do the task.
eleven_cupfuls · 2022-09-06 18:28
> every other language I’ve played with let’s you change the value of local variables within their local range.
The key is that a second `let` creates a _new_ scope ("range"), with a new variable. I don't know which languages you are familiar with, but in a generic curly brace language this:
var name = "Sam"
do {
var name = name
name = "Bill"
}
print(name) // "Sam", because the inner (shadowing)
// `name` no longer exists
would be equivalent to:
(let ((name "Sam"))
(let ((name name))
(setq name "Bill"))
(message "%s" name)) ;; "Sam" because the inner (shadowing)
;; `name` no longer exists
As other people have mentioned, if you want to change the value of the first binding from an inner scope, `setq` is the way:
var name = "Sam"
do {
name = "Bill"
}
print(name) // "Bill"
Elisp:
(let ((name "Sam"))
(let ()
(setq name "Bill"))
(message "%s" name)) ;; "Bill"
owmagow · 2022-09-06 19:04
Very very very helpful.
I understand.
Thank you.
xtifr · 2022-09-06 22:26
You can modify the string in place with `aset` or `store-substring`, but, in addition to being needlessly complicated, this is only of very limited use, as you cannot change the *length* of the string. Still, you can do:
(let ((foo "Sam"))
(aset foo 1 ?u)
foo)
And this will return "Sum". However, if you try to do:
(aset foo 3 ?l)
or
(store-substring foo 0 "Bill")
You will get an "out of range" error, because "Sam" only contains three letters.
Source: I opened the Emacs Lisp manual (`Help > More Manuals > Emacs Lisp Reference`), then went to the section "Strings and Characters" and, within that, to "Modifying Strings". :)
owmagow · 2022-09-06 22:39
Interesting. That's an odd sort of capability/limitation spec.
7890yuiop · 2022-09-07 04:38
> How do I change the value of a string variable within a let statement without resorting to using `setq`?
> For example … `(let ((name “Sam”)) (… something here to make name = “Bill”) )`
The question is unclear (mostly due to the aversion to `setq` which I would otherwise assume you wanted). Which binding of `name` do you want to change? Are you (1) looking to change "Sam" to "Bill"? Or are you (2) looking to change the value of `name` *outside* of the `let` scope to "Bill" whilst still seeing "Sam" within the `let` scope?
If you want (1) then "without resorting to using setq" indicates a misunderstanding -- you *should* use `setq` which will set the value of a variable for its current scope/binding.
If you want (2) then see `C-h f set-default-toplevel-value`
owmagow · 2022-09-07 04:50
Yep, a previous post made that clear (about setq).
I thought it would break the rules about local scope. But alas, I know understand.
owmagow · 2022-09-07 04:50
Yep, a previous post made that clear (about setq).
I thought it would break the rules about local scope. But alas, I now understand.
owmagow · 2022-09-07 04:51
Yep, a previous post made that clear (about setq).
I thought it would break the rules about local scope. But alas, I now understand.
7890yuiop · 2022-09-07 06:21
Not really; `aset` isn't a facility for editing strings, it's a facility for manipulating arrays (which strings happen to be), and arrays have a fixed size. Refer to `C-h i g (elisp)Arrays`
deaddyfreddy · 2022-09-07 08:00
it's not clear what you are trying to achieve
owmagow · 2022-09-07 13:45
Ah, good point.