unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
* Question about let binding behavior
@ 2024-10-08  6:21 Louis-Guillaume Gagnon
  2024-10-08  6:41 ` tomas
                   ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Louis-Guillaume Gagnon @ 2024-10-08  6:21 UTC (permalink / raw)
  To: help-gnu-emacs

Dear all,

I'm a long time user but I've only recently started to dive into elisp 
programming. I'm a bit surprised by the following behavior.

Let's say I write the following silly function:

(defun foo (bar)
   (let ((baz '((quux . 0) (quuz . 0))))
     (if (> 10 bar)
     (setcdr (assoc 'quux baz) bar)
       (setcdr (assoc 'quuz baz) bar))
     baz))

Then the following evals:

(foo 1)
=> ((quux . 1) (quuz . 0))

(foo 100)
=> ((quux . 1) (quuz . 100))

I'm surprised that the list is not reset since I thought the binding was 
local to the function, but clearly I don't have the right mental model 
for elisp evaluation. For the actual application I ended up using 
copy-tree but I'd like to understand this better -- could someone 
explain or point out the relevant sections of the manual?

Cheers,
L-G

P.S. Please keep me in CC, I'm not subscribed to this list





^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-08  6:21 Question about let binding behavior Louis-Guillaume Gagnon
@ 2024-10-08  6:41 ` tomas
  2024-10-08  7:49   ` Louis-Guillaume Gagnon
  2024-10-08 15:47 ` [External] : " Drew Adams
  2024-10-09  2:28 ` Stefan Monnier via Users list for the GNU Emacs text editor
  2 siblings, 1 reply; 16+ messages in thread
From: tomas @ 2024-10-08  6:41 UTC (permalink / raw)
  To: Louis-Guillaume Gagnon; +Cc: help-gnu-emacs

[-- Attachment #1: Type: text/plain, Size: 1348 bytes --]

On Tue, Oct 08, 2024 at 06:21:03AM +0000, Louis-Guillaume Gagnon wrote:
> Dear all,
> 
> I'm a long time user but I've only recently started to dive into elisp 
> programming. I'm a bit surprised by the following behavior.
> 
> Let's say I write the following silly function:
> 
> (defun foo (bar)
>    (let ((baz '((quux . 0) (quuz . 0))))
>      (if (> 10 bar)
>      (setcdr (assoc 'quux baz) bar)
>        (setcdr (assoc 'quuz baz) bar))
>      baz))
> 
> Then the following evals:
> 
> (foo 1)
> => ((quux . 1) (quuz . 0))
> 
> (foo 100)
> => ((quux . 1) (quuz . 100))
> 
> I'm surprised that the list is not reset since I thought the binding was 
> local to the function, but clearly I don't have the right mental model 
> for elisp evaluation.

The binding (i.e. the "thing" linking the symbol baz to the value
'(...) is local. But what you do with that (setcdr... ) is to change
the value itself. Your function returns this (possibly changed) value.

Lisps have a different "accent" than more pure functional languages,
where values are more immutable.

> For the actual application I ended up using 
> copy-tree but I'd like to understand this better -- could someone 
> explain or point out the relevant sections of the manual?

Did that help?

Cheers & enjoy Lisp :-)
-- 
tomás

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-08  6:41 ` tomas
@ 2024-10-08  7:49   ` Louis-Guillaume Gagnon
  2024-10-08  8:05     ` tomas
                       ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Louis-Guillaume Gagnon @ 2024-10-08  7:49 UTC (permalink / raw)
  To: tomas; +Cc: help-gnu-emacs

Hi Tomas,

Le 10/8/24 à 8:41 AM, tomas@tuxteam.de a écrit :
> The binding (i.e. the "thing" linking the symbol baz to the value
> '(...) is local. But what you do with that (setcdr... ) is to change
> the value itself. Your function returns this (possibly changed) value.
That makes sense, but I guess I'm more surprised that the list itself is 
only evaluated a single time -- I would naively have expected for the 
list to be created anew every time the function is called but that's 
evidently not what's happening.
> Did that help?

It did! Thanks.

Cheers,
L-G






^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-08  7:49   ` Louis-Guillaume Gagnon
@ 2024-10-08  8:05     ` tomas
  2024-10-08 16:02       ` tomas
  2024-10-08  8:15     ` Joost Kremers
  2024-10-08 22:21     ` Michael Heerdegen via Users list for the GNU Emacs text editor
  2 siblings, 1 reply; 16+ messages in thread
From: tomas @ 2024-10-08  8:05 UTC (permalink / raw)
  To: Louis-Guillaume Gagnon; +Cc: help-gnu-emacs

[-- Attachment #1: Type: text/plain, Size: 1348 bytes --]

On Tue, Oct 08, 2024 at 07:49:25AM +0000, Louis-Guillaume Gagnon wrote:
> Hi Tomas,
> 
> Le 10/8/24 à 8:41 AM, tomas@tuxteam.de a écrit :
> > The binding (i.e. the "thing" linking the symbol baz to the value
> > '(...) is local. But what you do with that (setcdr... ) is to change
> > the value itself. Your function returns this (possibly changed) value.
> That makes sense, but I guess I'm more surprised that the list itself is 
> only evaluated a single time -- I would naively have expected for the 
> list to be created anew every time the function is called but that's 
> evidently not what's happening.

Oh. It is a constant (well, technically speaking "a literal" -- constant
has other meanings around here). This is another rabbit hole :-)

But -- either someone else chimes in, or it'll have to wait till the
afternoon, since I'm at $DAYJOB at the moment.

The short answer is that it's usually a good idea to use "fresh" storage
when you plan to mutate the (innards of the) "thing".

Some Lisps enforce literal immutability. Some others do funny things.

I.e. some moral equivalent of

  (let ((foo (cons (cons one 1) (cons two 2)))) ...)

(of course you may use some deep-copy function or similar).

The fun part is that you get to choose at which depth you plan in
immutability :-)

Cheers
-- 
t

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-08  7:49   ` Louis-Guillaume Gagnon
  2024-10-08  8:05     ` tomas
@ 2024-10-08  8:15     ` Joost Kremers
  2024-10-08 22:27       ` Michael Heerdegen via Users list for the GNU Emacs text editor
  2024-10-08 22:21     ` Michael Heerdegen via Users list for the GNU Emacs text editor
  2 siblings, 1 reply; 16+ messages in thread
From: Joost Kremers @ 2024-10-08  8:15 UTC (permalink / raw)
  To: Louis-Guillaume Gagnon; +Cc: tomas, help-gnu-emacs

On Tue, Oct 08 2024, Louis-Guillaume Gagnon wrote:
> That makes sense, but I guess I'm more surprised that the list itself is 
> only evaluated a single time -- I would naively have expected for the 
> list to be created anew every time the function is called but that's 
> evidently not what's happening.

The reason for that is that the list is a constant: the literal list is
part of the code, and the object is created when the defun is evaluated.
The object will stick around, even if the binding to it (baz) is local to
the function.

Now, what I honestly don't know is if the object can at some point be
garbage-collected, but I suspect it can. I wouldn't bet on it staying
around, anyway.

If you don't want the object to stick around, make sure you create it in
the function:

```
(defun foo (bar)
  (let ((baz (list (cons 'quux 0) (cons 'quuz 0))))
    (if (> 10 bar)
        (setcdr (assoc 'quux baz) bar)
      (setcdr (assoc 'quuz baz) bar))
    baz))
```



-- 
Joost Kremers
Life has its moments



^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [External] : Question about let binding behavior
  2024-10-08  6:21 Question about let binding behavior Louis-Guillaume Gagnon
  2024-10-08  6:41 ` tomas
@ 2024-10-08 15:47 ` Drew Adams
  2024-10-09  2:28 ` Stefan Monnier via Users list for the GNU Emacs text editor
  2 siblings, 0 replies; 16+ messages in thread
From: Drew Adams @ 2024-10-08 15:47 UTC (permalink / raw)
  To: Louis-Guillaume Gagnon, help-gnu-emacs@gnu.org

https://emacs.stackexchange.com/questions/45820/when-to-use-quote-for-lists-modifying-quoted-lists-in-elisp


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-08  8:05     ` tomas
@ 2024-10-08 16:02       ` tomas
  2024-10-09  8:01         ` Louis-Guillaume Gagnon via Users list for the GNU Emacs text editor
  0 siblings, 1 reply; 16+ messages in thread
From: tomas @ 2024-10-08 16:02 UTC (permalink / raw)
  To: Louis-Guillaume Gagnon; +Cc: help-gnu-emacs

[-- Attachment #1: Type: text/plain, Size: 2201 bytes --]

On Tue, Oct 08, 2024 at 10:05:16AM +0200, tomas@tuxteam.de wrote:
> On Tue, Oct 08, 2024 at 07:49:25AM +0000, Louis-Guillaume Gagnon wrote:
> > Hi Tomas,
> > 
> > Le 10/8/24 à 8:41 AM, tomas@tuxteam.de a écrit :
> > > The binding (i.e. the "thing" linking the symbol baz to the value
> > > '(...) is local. But what you do with that (setcdr... ) is to change
> > > the value itself. Your function returns this (possibly changed) value.
> > That makes sense, but I guess I'm more surprised that the list itself is 
> > only evaluated a single time -- I would naively have expected for the 
> > list to be created anew every time the function is called but that's 
> > evidently not what's happening.
> 
> Oh. It is a constant (well, technically speaking "a literal" -- constant
> has other meanings around here). This is another rabbit hole :-)

Actually, the Emacs Lisp manual has a thorough explanation for that
("Lisp Data Types > Mutability")

Here's an excerpt, but the whole section is worth a read:

  2.9 Mutability
  ==============
  
  Some Lisp objects should never change.  For example, the Lisp expression
  ‘"aaa"’ yields a string, but you should not change its contents.  And
  some objects cannot be changed; for example, although you can create a
  new number by calculating one, Lisp provides no operation to change the
  value of an existing number.
  
     Other Lisp objects are “mutable”: it is safe to change their values
  [...]
  
     If a program attempts to change objects that should not be changed,
  the resulting behavior is undefined: the Lisp interpreter might signal
  an error, or it might crash or behave unpredictably in other ways.(1)
  
     When similar constants occur as parts of a program, the Lisp
  interpreter might save time or space by reusing existing constants or
  their components.  For example, ‘(eq "abc" "abc")’ returns ‘t’ if the
  interpreter creates only one instance of the string literal ‘"abc"’, and
  returns ‘nil’ if it creates two instances.  Lisp programs should be
  written so that they work regardless of whether this optimization is in
  use.

Cheers
-- 
t

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-08  7:49   ` Louis-Guillaume Gagnon
  2024-10-08  8:05     ` tomas
  2024-10-08  8:15     ` Joost Kremers
@ 2024-10-08 22:21     ` Michael Heerdegen via Users list for the GNU Emacs text editor
  2 siblings, 0 replies; 16+ messages in thread
From: Michael Heerdegen via Users list for the GNU Emacs text editor @ 2024-10-08 22:21 UTC (permalink / raw)
  To: help-gnu-emacs

Louis-Guillaume Gagnon <gagnonlg@protonmail.com> writes:

> That makes sense, but I guess I'm more surprised that the list itself
> is only evaluated a single time -- I would naively have expected for
> the list to be created anew every time the function is called but
> that's evidently not what's happening.

Some background: This is one of the most surprising things in Elisp.
Using literal lists in code can produce hard to detect bugs.  The
phenomenon has the name "self modifying code".

BTW, considering evaluation alone doesn't suffice to understand what is
going on here - we have to step back and start with the Lisp reader.
Reading the expression

  '((quux . 0) (quuz . 0))

will create a quoted list literal: it is the reader that creates the
list ((quux . 0) (quuz . 0)) _once_.  This list literal gets part of
your code, with a quote in front of it.  That quote prevents evaluation.

The weird thing here is that when the code is running it has access to
that list that is part of the code at the same time.  The object is part
of two different levels.

Please think about it and try hard not to forget: (list ELEMENT ...) and
'(ELEMENT) are _not_ equivalent, the latter is not simply an
abbreviation of the former.  Whenever you use a quoted list in your
code, be sure to avoid this pitfall.  If your list literal might be
modified destructively when running the code, maybe even as part of a
larger structure (as a sublist maybe), better let your code construct a
fresh list.

Also keep in mind that `backquote'd expressions also create list
literals.  There is an analogue pitfall for backquote.


Michael.




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-08  8:15     ` Joost Kremers
@ 2024-10-08 22:27       ` Michael Heerdegen via Users list for the GNU Emacs text editor
  0 siblings, 0 replies; 16+ messages in thread
From: Michael Heerdegen via Users list for the GNU Emacs text editor @ 2024-10-08 22:27 UTC (permalink / raw)
  To: help-gnu-emacs

Joost Kremers <joostkremers@fastmail.fm> writes:

> Now, what I honestly don't know is if the object can at some point be
> garbage-collected, but I suspect it can. I wouldn't bet on it staying
> around, anyway.

Code is data... as long as it is part of executable code, it is clearly
reachable from Elisp (e.g. via `symbol-function') so it will not be
garbage collected.  If it is not reachable, it also can't be executed so
it doesn't matter.

Michael.




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-08  6:21 Question about let binding behavior Louis-Guillaume Gagnon
  2024-10-08  6:41 ` tomas
  2024-10-08 15:47 ` [External] : " Drew Adams
@ 2024-10-09  2:28 ` Stefan Monnier via Users list for the GNU Emacs text editor
  2 siblings, 0 replies; 16+ messages in thread
From: Stefan Monnier via Users list for the GNU Emacs text editor @ 2024-10-09  2:28 UTC (permalink / raw)
  To: help-gnu-emacs

>    (let ((baz '((quux . 0) (quuz . 0))))
>      (if (> 10 bar)
>      (setcdr (assoc 'quux baz) bar)
>        (setcdr (assoc 'quuz baz) bar))
>      baz))
>
> Then the following evals:
>
> (foo 1)
> => ((quux . 1) (quuz . 0))
>
> (foo 100)
> => ((quux . 1) (quuz . 100))

Does `C-h f quote RET` answer your question?
(`quote` is the full-name of the ' you used in your code).


        Stefan




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-08 16:02       ` tomas
@ 2024-10-09  8:01         ` Louis-Guillaume Gagnon via Users list for the GNU Emacs text editor
  2024-10-09  8:13           ` Tomas Hlavaty
  0 siblings, 1 reply; 16+ messages in thread
From: Louis-Guillaume Gagnon via Users list for the GNU Emacs text editor @ 2024-10-09  8:01 UTC (permalink / raw)
  To: tomas; +Cc: help-gnu-emacs

Hi Tomas,

Le 10/8/24 à 6:02 PM, tomas@tuxteam.de a écrit :

> If a program attempts to change objects that should not be changed,
>   the resulting behavior is undefined: the Lisp interpreter might signal
>   an error, or it might crash or behave unpredictably in other ways.(1)
>
>      When similar constants occur as parts of a program, the Lisp
>   interpreter might save time or space by reusing existing constants or
>   their components.  For example, ‘(eq "abc" "abc")’ returns ‘t’ if the
>   interpreter creates only one instance of the string literal ‘"abc"’, and
>   returns ‘nil’ if it creates two instances.  Lisp programs should be
>   written so that they work regardless of whether this optimization is in
>   use.

So IIUC when I write "(let ((baz '((quux . 0) (quuz . 0)))) [...]", I'm creating a constant & binding it to 'baz'. The interpreter is free to use the same underlying list whenever I refer to this constant, & in this case, it survives across calls to the function. My program relied on modifying the constant and expecting those changes not to persist between calls, which means the program is ill-formed. By using "copy-tree" on the constant, I'm instructing the interpreter to give me a fresh copy of the constant at every call, which is required to get the behavior I expect.

Thanks for pointing out the section, I'll definitely read it

Cheers
L-G

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-09  8:01         ` Louis-Guillaume Gagnon via Users list for the GNU Emacs text editor
@ 2024-10-09  8:13           ` Tomas Hlavaty
  2024-10-09 23:39             ` Stefan Monnier via Users list for the GNU Emacs text editor
  0 siblings, 1 reply; 16+ messages in thread
From: Tomas Hlavaty @ 2024-10-09  8:13 UTC (permalink / raw)
  To: Louis-Guillaume Gagnon, tomas; +Cc: help-gnu-emacs

On Wed 09 Oct 2024 at 08:01, Louis-Guillaume Gagnon via Users list for the GNU Emacs text editor <help-gnu-emacs@gnu.org> wrote:
> The interpreter is free to use the same underlying list whenever I
> refer to this constant

compiler too

> My program relied on modifying the constant

don't do that

> By using "copy-tree" on the constant

or you can avoid the constant in the first place, for example with
minimal change like this:

   (let ((baz `((quux . 0) (quuz . 0))))



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-09  8:13           ` Tomas Hlavaty
@ 2024-10-09 23:39             ` Stefan Monnier via Users list for the GNU Emacs text editor
  2024-10-10  7:58               ` Tomas Hlavaty
  0 siblings, 1 reply; 16+ messages in thread
From: Stefan Monnier via Users list for the GNU Emacs text editor @ 2024-10-09 23:39 UTC (permalink / raw)
  To: help-gnu-emacs

> or you can avoid the constant in the first place, for example with
> minimal change like this:
>
>    (let ((baz `((quux . 0) (quuz . 0))))

Hmm... I think this makes no difference: ` does not guarantee it returns
a different object each time.
And indeed, if you try:

    (macroexpand '`((quux . 0) (quuz . 0)))
=>
    '((quux . 0) (quuz . 0))


- Stefan




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-09 23:39             ` Stefan Monnier via Users list for the GNU Emacs text editor
@ 2024-10-10  7:58               ` Tomas Hlavaty
  2024-10-10 18:38                 ` Stefan Monnier via Users list for the GNU Emacs text editor
  0 siblings, 1 reply; 16+ messages in thread
From: Tomas Hlavaty @ 2024-10-10  7:58 UTC (permalink / raw)
  To: Stefan Monnier, help-gnu-emacs

On Wed 09 Oct 2024 at 19:39, Stefan Monnier via Users list for the GNU Emacs text editor <help-gnu-emacs@gnu.org> wrote:
>> or you can avoid the constant in the first place, for example with
>> minimal change like this:
>>
>>    (let ((baz `((quux . 0) (quuz . 0))))
>
> Hmm... I think this makes no difference: ` does not guarantee it returns
> a different object each time.
> And indeed, if you try:
>
>     (macroexpand '`((quux . 0) (quuz . 0)))
> =>
>     '((quux . 0) (quuz . 0))

ok, try (let ((baz `((quux . ,0) (quuz . ,0))))

(macroexpand '`((quux . ,0) (quuz . ,0)))
=>
(list (cons 'quux 0) (cons 'quuz 0))

although sbcl does not get tricked and requires

CL-USER> (macroexpand '`((quux . ,a) (quuz . ,b)))
(LIST (LIST* 'QUUX A) (LIST* 'QUUZ B))
T

(defun foo2 (bar &optional (a 0) (b 0))
  (let ((baz `((quux . ,a) (quuz . ,b))))
    (if (> 10 bar)
        (setf (cdr (assoc 'quux baz)) bar)
        (setf (cdr (assoc 'quuz baz)) bar))
    baz))
(foo2 1)
(foo2 100)

in emacs-lisp:

(defun foo2 (bar &optional a b)
  (let ((baz `((quux . ,(or a 0)) (quuz . ,(or b 0)))))
    (if (> 10 bar)
        (setcdr (assoc 'quux baz) bar)
      (setcdr (assoc 'quuz baz) bar))
    baz))
(foo2 1)
(foo2 100)

although this also works in emacs-lisp (for now?):

(defun foo3 (bar)
  (let* ((a 0)
         (b 0)
         (baz `((quux . ,a) (quuz . ,b))))
    (if (> 10 bar)
        (setcdr (assoc 'quux baz) bar)
      (setcdr (assoc 'quuz baz) bar))
    baz))
(foo3 1)
(foo3 100)



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-10  7:58               ` Tomas Hlavaty
@ 2024-10-10 18:38                 ` Stefan Monnier via Users list for the GNU Emacs text editor
  2024-10-18 11:25                   ` Rudolf Schlatte
  0 siblings, 1 reply; 16+ messages in thread
From: Stefan Monnier via Users list for the GNU Emacs text editor @ 2024-10-10 18:38 UTC (permalink / raw)
  To: help-gnu-emacs

> although this also works in emacs-lisp (for now?):
                                         ^^^^^^^^^^
                                         +1

IOW, if you need it to be a fresh new data-structure, use `cons/list`.


        Stefan




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about let binding behavior
  2024-10-10 18:38                 ` Stefan Monnier via Users list for the GNU Emacs text editor
@ 2024-10-18 11:25                   ` Rudolf Schlatte
  0 siblings, 0 replies; 16+ messages in thread
From: Rudolf Schlatte @ 2024-10-18 11:25 UTC (permalink / raw)
  To: help-gnu-emacs

Stefan Monnier via Users list for the GNU Emacs text editor
<help-gnu-emacs@gnu.org> writes:

>> although this also works in emacs-lisp (for now?):
>                                          ^^^^^^^^^^
>                                          +1
>
> IOW, if you need it to be a fresh new data-structure, use `cons/list`.

Or call `copy-tree' on the constant, as the OP did in the end IIRC.




^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2024-10-18 11:25 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-08  6:21 Question about let binding behavior Louis-Guillaume Gagnon
2024-10-08  6:41 ` tomas
2024-10-08  7:49   ` Louis-Guillaume Gagnon
2024-10-08  8:05     ` tomas
2024-10-08 16:02       ` tomas
2024-10-09  8:01         ` Louis-Guillaume Gagnon via Users list for the GNU Emacs text editor
2024-10-09  8:13           ` Tomas Hlavaty
2024-10-09 23:39             ` Stefan Monnier via Users list for the GNU Emacs text editor
2024-10-10  7:58               ` Tomas Hlavaty
2024-10-10 18:38                 ` Stefan Monnier via Users list for the GNU Emacs text editor
2024-10-18 11:25                   ` Rudolf Schlatte
2024-10-08  8:15     ` Joost Kremers
2024-10-08 22:27       ` Michael Heerdegen via Users list for the GNU Emacs text editor
2024-10-08 22:21     ` Michael Heerdegen via Users list for the GNU Emacs text editor
2024-10-08 15:47 ` [External] : " Drew Adams
2024-10-09  2:28 ` Stefan Monnier via Users list for the GNU Emacs text editor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).