From: Roos Van Raadshooven L.A. (Leon)
Subject: proper lisp expression?
Date: 
Message-ID: <roosvanr.931285725@biceps>
Hi Lispers,

In lisp applications counting words using hashtables i frequently
use the following expression to increment counters in the table.
I use it on uninitialized entries too. The expression:

	(incf (gethash the-word the-hash-table 0))

Although it works in ACL, i'm not sure if this is "proper lisp".
Is it OK to assume an initialized "place" for the incf in the above?
Any comments?

Leon.

From: Barry Margolin
Subject: Re: proper lisp expression?
Date: 
Message-ID: <Prsg3.1165$KM3.276620@burlma1-snr2>
In article <··················@biceps>,
Roos Van Raadshooven L.A. (Leon) <········@storm.research.kpn.com> wrote:
>Hi Lispers,
>
>In lisp applications counting words using hashtables i frequently
>use the following expression to increment counters in the table.
>I use it on uninitialized entries too. The expression:
>
>	(incf (gethash the-word the-hash-table 0))
>
>Although it works in ACL, i'm not sure if this is "proper lisp".
>Is it OK to assume an initialized "place" for the incf in the above?
>Any comments?

The description of GETHASH says:

  When a gethash form is used as a setf place, any default which is
  supplied is evaluated according to normal left-to-right evaluation rules,
  but its value is ignored.

However, I'm not quite sure how this is intended to be applied in the case
of a modifying macro like INCF.  Should the INCF be treated like:

(setf (gethash the-word the-hash-table 0)
      (1+ (gethash the-word the-hash-table 0)))

in which the default is ignored in the first subform but used in the
second?  I would be wary of depending on this, as the necessity to avoid
double evaluation makes it more likely that the SETF Expander for GETHASH
evaluates the default and then throws it away in both places.

There are also implementations of SETF that make use of locatives; (locf
(gethash nonexistent-key table 0)) probably doesn't return a valid
location.

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Kent M Pitman
Subject: Re: proper lisp expression?
Date: 
Message-ID: <sfw1zel4kho.fsf@world.std.com>
Barry Margolin <······@bbnplanet.com> writes:

> However, I'm not quite sure how this is intended to be applied in the case
> of a modifying macro like INCF.  Should the INCF be treated like:
> 
> (setf (gethash the-word the-hash-table 0)
>       (1+ (gethash the-word the-hash-table 0)))

FWIW, I use this from time to time and expect it to work.
I've never seen it not work.

You're probably right that people should tread lightly, but I also
think vendors should work hard to make this work because it's so
incredibly handy.

I think the value being ignored part is not supposed to be referring
to the implementation via define-setf-expander, but rather the
high-level thing.  That is, the expression you hvae indicated above.

The reason it wants to say the value is ignored is to say that it
doesn't do the thing which on some days I think might have been
the better thing which is to not use any storage in case the default
is the same as the given value.  That is, does:

 (let ((x (make-hash-table)))
   (setf (gethash :foo x 0) 0)
   x)

return an empty hash table.  I think it does not because the spec
says the third arg to gethash is ignored when setting; for it not to
be ignored might imply that if the value stored was the same as the
default, no storage needed to happen since a later (gethash :foo x 0)
would still retrieve it.  We didn't go for semantics anywhere near
as clever as this, but that protects against the bug of someone
using a different default elsewhere and getting confused.
From: Steven and Chie Haflich
Subject: Re: proper lisp expression?
Date: 
Message-ID: <37844FCC.3BF4D157@franz.com>
Kent M Pitman wrote:

> FWIW, I use this from time to time and expect it to work.
> I've never seen it not work.
> 
> You're probably right that people should tread lightly, but I also
> think vendors should work hard to make this work because it's so
> incredibly handy.

I think the ANS guarantees that it works.

INCF is defined thus:

  The delta is added to ... the number in place and the result
  is stored in place. 

To me this implies that the value in place must be referenced before
the addition, and then the new value is stored.  It is correct that
the optional default value argument to setf of gethash is not used,
the expansion incf of gethash does both a reference and then a set.
An implementation may optimize the repeated reference (e.g. with
locatives) but IMO it must preserve the semantics.

> I think the value being ignored part is not supposed to be referring
> to the implementation via define-setf-expander, but rather the
> high-level thing.  That is, the expression you hvae indicated above.
> 
> The reason it wants to say the value is ignored is to say that it
> doesn't do the thing which on some days I think might have been
> the better thing which is to not use any storage in case the default
> is the same as the given value.  That is, does:
> 
>  (let ((x (make-hash-table)))
>    (setf (gethash :foo x 0) 0)
>    x)
> 
> return an empty hash table.  I think it does not because the spec
> says the third arg to gethash is ignored when setting; for it not to
> be ignored might imply that if the value stored was the same as the
> default, no storage needed to happen since a later (gethash :foo x 0)
> would still retrieve it.  We didn't go for semantics anywhere near
> as clever as this, but that protects against the bug of someone
> using a different default elsewhere and getting confused.

I've already claimed that this kind of optimization violates the
required semantics, although reasonable minds might disagree.
Optimization using locatives needs be careful, because the default
value form can legitimately remhash the key, e.g.:

   (incf (getf my-sym ht (progn (remhash 'foo ht) 0)))

IMO this code needs to do the obvious thing if my-sym is bound to
the symbol foo.  No, I don't usually write code like this...