Subject: RE: bug#28753: 25.3; Functions to get alist from hash table and vice versa

From: Drew Adams <[email-masked]>
Date: Thu, 12 Oct 2017 07:36:52 -0700 (PDT)

(Odd: I (and the bug-list?) don't seem to have received the
reply from Michael that you quote below.)

> Michael Heerdegen <[email-masked]> writes:
>
> > >> Actually, going from alist to hash table doesn't look so
> >> useful with `map-into'.  A caller should be able to specify
> >> the hash-table parameters (features), such as :test.
> >
> > Yeah, that's what I thought, too.
> 
> That's something I can easily add.  `map-into' would then look
> like the following `(defun map-into (map type &rest keywords))'.

&rest keywords does not treat the keywords as keywords.
That's why we have (CL has) &keys.  (And &rest can be
used together with &keys.)

> However, `keywords' would be ignored when converting to
> an alist.  I'm not sure I like it.

Converting to an alist is different from converting to
a hash table.  As I mentioned, in particular a hash table
has, as part of its definition, a :test function etc.
An alist does not.  Even an alist in CL does not, even
though CL at least allows :test with `assoc'.

So a general function that wants to handle the two the
same way is out of luck.  The best it can do is just
allow whatever keyword args or other args a given
target representation construction function might need.
But in that case it cannot actually control such args
in the call.

See the code I sent originally.

Note too the optional arg USE-LAST-P that I provided
for conversion of an alist to a hash-table.

Unlike a hash table, an alist is often used in a way
that allows multiple entries for the same key, and
in particular in a way where the first entry shadows
the other entries for the same key.

That means that, for that common alist use case, it
is likely that the conversion to a hash table would
use only the first alist entry with a given key,
ignoring any others.

But because an alist can be used in multiple ways,
including ways where the other entries (shadowed in
the typical case) are relevant, and because the
most common case among those cases is for the last
entry, not the first one, to be most relevant, we
provide arg USE-LAST-P.  When that arg is non-nil
the last alist entry for a given key is used for
the hash table.

(If you get a hash table from an alist then you
necessarily get only one entry for a given key.
So you do not really capture the alist in all its
functionality.  Presumably someone asking for such
a conversion knows this.  We could, instead of
providing an optional arg such as USE-LAST-P, just
expect the user who wants the hash table to have
already removed all alist entries with the same
key except for the entry s?he wants.  But that
can be a bit of a bother, and it's easy to
provide USE-LAST-P as a convenience.)

It's great to have abstract, general-purpose
functions that handle maps, sequences, streams,
etc.  But not all such things are handled the
same way in Lisp code.  An alist is itself quite
general, so it can be and is used in different
ways in different programs.  There is no single,
simple mapping from an arbitrary alist to a hash
table, I think.

We can provide a general-purpose function that
chooses just one kind of mapping from an alist
to, e.g., a hash table.  But I'm guessing that we
should also provide a function that gives you more
control over the conversion mapping.  IOW, maybe
both a `map-into' kind of general-purpose behavior
and a more specific `alist-to-hash-table' kind of
behavior.

(But for the time being, `map-into' is only for
list <-> hash table, so for the time being it
seems less useful than the more specific
function.)