libera/#commonlisp - IRC Chatlog
Search
8:18:37
cpli
i don't know why there even is a different macro for LET* if it makes LET entirely superfluous
8:24:58
hayley
LET provides "parallel" evaluation. Not as in that the values of the bound variables are produced in parallel, but that they don't observe each other.
8:25:04
beach
It doesn't make LET superfluous. You might want something like (LET ((X ...) (Y (F X))) to make X refer to an outer binding.
8:26:05
beach
cpli: But for this particular case, i.e., when the initform in a binding refers to a variable in a previous binding, LET* is the thing to use.
8:35:02
hayley
(We only have LET and LETREC in the system I'm co-designing; the latter behaves "close enough", except for when it doesn't, of course.)
9:59:34
cpli
beach i'm sorry, it does make sense to me. but being able to occasionally shadow a symbol seems far second to being able to use the symbols that you just defined
10:00:21
cpli
the fact that X may refer to an outer binding appears to me so situational and so niche i find it ludicrous for you to bring it up at all
10:01:03
splittist
cpli: using a LET rather than a LET* also signals to the human reader of the code that you are NOT referring to variables introduced in that LET, which aids in comprehension.
10:01:53
cpli
when was the last time you had deliberately referred to X after a LET-clause "declaring" that symbol?
10:02:28
cpli
i.e. referring to an outer binding after assigning to the same symbol for the body of the LET
10:03:00
cpli
splittist: then does splitting { a = f; b = g a, c = h a } into two LETs instead of one LET* not make sense?
10:03:46
jackdaniel
does it really matter if it is semantically correct and you prefer nested let (or single let*)?
10:04:58
cpli
D:< this is ultimately important! how dare you reduce my manic rambles over pragmatically equivalent code to "bike sheds"
10:07:25
hayley
() and NIL read as the same object (barring infrequently asked questions-esque frobbery), and NIL evaluates to itself. So it follows () also evaluates to itself.
10:09:19
splittist
cpli: I, personally, myself, for me, think of it this way: LET means I'm introducing some names in a scope (not using these terms with any particular precision), and LET* means I'm doing that AND ALSO IN ADDITION defining one or more of those names in terms of others. So it's a reminder to me of my intention. I don't use nested lets without intervening code, but that's just a habit, I guess. Sometimes I wonder about using one of
10:12:20
beach
LET signals to the reader that the variables introduced are not part of the any of the forms.
10:12:23
cpli
i.e. i can replace this and only this with something less mind-numbing (LET ((A (F))) (LET ((B (G A))) (LET ((C (H B))) ; ...
10:13:55
cpli
i.e. discrete. it seems strange to me that when LET is taught as "simultaneous" bindings that one would cast away the direct structure in favor of 2 spaces of indentation
10:14:30
beach
Indentation is not the argument. It is the argument that splittist gave, and that I repeated.
10:16:02
cpli
you argue (LET* ((THING (PREPARE-THING)) (FIELDA (GET-FIELDA THING)) (FIELDB (GET-FIELDB THING))...
10:18:11
cpli
in my opinion: (LET* ((THING (PREPARE-THING)) (FIELDA (GET-FIELDA THING)) (FIELDB (GET-FIELDB THING)) ;.. omitted
10:18:24
beach
For that case, it depends on how many "fields" you have, so either (let ((thing ...)) (let* ((fielda ...) (fieldb ...)) ...)...), or a big LET* if you have few "fields".
10:18:57
cpli
casts away the direct dependency all notion of dependency of FIELDA, FIELDB, .. to THING
10:19:58
cpli
beach exactly not LET* in the former case. FIELDA, FIELDB, etc all depend only on THING
10:21:15
cpli
if people don't care about expressing the relationships between bindings in LET*, then why have LET
10:23:32
splittist
A little bit of sequential processing in a LET* is OK. If it gets too long, a macro might make the intention clearer, eg. something like ~> for purely sequential processing or a special destructuring macro if that's what you're doing (or not bother destructuring)
10:23:42
jackdaniel
cpli: (let ((x1 (round x1)) (x2 (round x2)) (real-dist (round (- x2 x1))) ...) has quite a different meaning from when you use let*
10:26:06
beach
cpli: People do care which is why LET exists, and LET* is used pretty much only in the sequential case. I think that's what I have been saying.
10:26:59
beach
cpli: The default is LET when it works, for the reason that splittist said. It signals to the person reading the code that there are no dependencies between the bindings, so the person does not have to verify that.
10:27:19
jackdaniel
cpli: just use your aesthetics sense to decide what looks better - I think that there are more interesting dillemas when writing programs ;p /me gets back to more interesting dillemas
11:12:31
_death
when I see a LET* (used according to the convention) it evokes a stronger sense of opportunity to re-factor
16:12:23
beach
Different question: Say we have (let* ((x ...) (y ...)) (declare ... x)...) and suppose the initialization form for y references x. Does the scope of the declaration include the initialization form for y? And if so, where in the standard does it say that?
16:15:37
beach
The scope of a bound declaration is the same as the lexical scope of the binding to which it applies.