From: Will Fitzgerald
Subject: Hash tables with weak keys
Date: 
Message-ID: <661755b5.0207190735.7e0a2f9c@posting.google.com>
I was writing some code this morning and wanted to create a hash table
with weak keys*. This isn't part of the Common Lisp standard, but most
vendors support it. Unfortunately, vendors have different syntax for
this.

It would be nice to have a cross-implementation way of doing this, for
example:


make-weak-hash-table &key  weak test size rehash-size rehash-threshold
=> hash-table


test, size, rehash-size, and rehash-threshold would be as for
MAKE-HASH-TABLE.

WEAK would be one of:

NIL -- no weak keys
:KEYS -- An entry is key if there are pointers to the key
:VALUES -- An entry is key if there are pointers to the value
:BOTH --  An entry is kept if there are pointers to both the key and
the value.
:EITHER -- An entry is kept if there are pointers to either the key
and the value.

(This is, more or less, the syntax from Lispworks, according to the
online documentations).

Here are my first attempts at writing this. Other implementations are
requested.

(defvar *default-ht-size* 
    (hash-table-size (make-hash-table)))
(defvar *default-ht-rehash-size* 
    (hash-table-rehash-size (make-hash-table)))
(defvar *default-ht-rehash-threshold* 
    (hash-table-rehash-threshold (make-hash-table)))
  
#+allegro 
(defun make-weak-hash-table 
    (&key weak 
	  (test #'eql)
	  (size *default-ht-size*)
	  (rehash-size *default-ht-rehash-size*)
	  (rehash-threshold *default-ht-rehash-threshold*))
  (when (eq weak :either)
    (setq weak :both)
    (cerror "Use :BOTH instead." 
	    "Allegro does not support :EITHER-type weak hash tables."))
  (ecase weak
    ((NIL)
     (make-hash-table  :test test 
		       :size size 
		       :rehash-size rehash-size 
		       :rehash-threshold rehash-threshold))
    ((:keys)
     (make-hash-table  :test test 
		       :size size 
		       :rehash-size rehash-size 
		       :rehash-threshold rehash-threshold
		       :weak-keys t))
    ((:values)
     (make-hash-table  :test test 
		       :size size 
		       :rehash-size rehash-size 
		       :rehash-threshold rehash-threshold
		       :values :weak))
    ((:both)
     (make-hash-table  :test test 
		       :size size 
		       :rehash-size rehash-size 
		       :rehash-threshold rehash-threshold
		       :weak-keys t
		       :values :weak))))


#+lispworks
(defun make-weak-hash-table
    (&key weak 
	  (test #'eql)
	  (size *default-ht-size*)
	  (rehash-size *default-ht-rehash-size*)
	  (rehash-threshold *default-ht-rehash-threshold))
  (ecase weak
    ((:values keys :both :either)
     (let ((ht (make-hash-table  :test test 
				 :size size 
				 :rehash-size rehash-size 
				 :rehash-threshold rehash-threshold
				 :weak-keys t)))
       (set-hash-table-weak ht weak)
       ht))))

#-(or allegro lispworks)
(defun make-weak-hash-table 
    (&key weak 
	  (test #'eql)
	  (size *default-ht-size*)
	  (rehash-size *default-ht-rehash-size*)
	  (rehash-threshold *default-ht-rehash-threshold))
  (declare (ignore weak))
  (cerror "Make 'non-weak' hash table instead."
	  "This implementation doesn't (yet) support weak hash-tables")
  (make-hash-table  :test test 
		    :size size 
		    :rehash-size rehash-size 
		    :rehash-threshold rehash-threshold))


* More or less, for a hash table with weak keys means that a key/value
entry in the hash table will be garbage-collected if the only
'pointer' to the key is the entry in the hash table.

From: Dave Bakhash
Subject: Re: Hash tables with weak keys
Date: 
Message-ID: <c29sn2fy1fv.fsf@no-knife.mit.edu>
··········@inetmi.com (Will Fitzgerald) writes:

> #+allegro 
> (defun make-weak-hash-table 
>     (&key weak 
> 	  (test #'eql)
> 	  (size *default-ht-size*)
> 	  (rehash-size *default-ht-rehash-size*)
> 	  (rehash-threshold *default-ht-rehash-threshold*))
>  ...    

and then

> #+lispworks
> (defun make-weak-hash-table
>     (&key weak 
> 	  (test #'eql)
> 	  (size *default-ht-size*)
> 	  (rehash-size *default-ht-rehash-size*)
> 	  (rehash-threshold *default-ht-rehash-threshold))
> ...

etc. etc.

What you want is to put the #+ stuff inside the defun, so that you don't
have so many repeated function headers.

dave
From: Will Fitzgerald
Subject: Re: Hash tables with weak keys
Date: 
Message-ID: <4B0%8.14204$t%5.6842131@newssvr28.news.prodigy.com>
"Dave Bakhash" <·····@alum.mit.edu> wrote in message
····················@no-knife.mit.edu...
> ··········@inetmi.com (Will Fitzgerald) writes:
>
> > #+allegro
> > (defun make-weak-hash-table
> >     (&key weak
> >   (test #'eql)
> >   (size *default-ht-size*)
> >   (rehash-size *default-ht-rehash-size*)
> >   (rehash-threshold *default-ht-rehash-threshold*))
> >  ...
>
> and then
>
> > #+lispworks
> > (defun make-weak-hash-table
> >     (&key weak
> >   (test #'eql)
> >   (size *default-ht-size*)
> >   (rehash-size *default-ht-rehash-size*)
> >   (rehash-threshold *default-ht-rehash-threshold))
> > ...
>
> etc. etc.
>
> What you want is to put the #+ stuff inside the defun, so that you don't
> have so many repeated function headers.
>
> dave


Mostly I put them where they were so it is abundantly clear to readers of
c.l.l that what I was looking for are different implementations.

But apparently, I didn't do a very good job as no one has found it a useful
thing to reply to.

- Will Fitzgerald
From: Thomas F. Burdick
Subject: Re: Hash tables with weak keys
Date: 
Message-ID: <xcv8z4110a3.fsf@conquest.OCF.Berkeley.EDU>
"Will Fitzgerald" <·················@ameritech.net.no.spam.please> writes:

> But apparently, I didn't do a very good job as no one has found it a useful
> thing to reply to.

Well, here's my $0.02 ... I don't think it's a good interface.  It's
just renaming the Lispworks MAKE-HASH-TABLE function, without
considering what it would take to make this work in other
implementations.  CMUCL and CLISP, for example, both have weak hash
tables, but only weak keys.  So to emulate weak entries, you'll need
to use their weak pointer facilities.  Which means you'll need to wrap
calls to GETHASH and (SETF GETHASH), and the entry-weak hash tables
can't be passed to code that doesn't know about them.  A complete
interface would need to be more like:

  WEAK-HASH:MAKE-HASH-TABLE (function)
  WEAK-HASH:STANDARD-HASH-TABLE (class)
  WEAK-HASH:WEAK-HASH-TABLE (class)
  WEAK-HASH:GETHASH (generic function)
  (SETF WEAK-HASH:GETHASH) (generic function)

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'