all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* bug#28753: 25.3; Functions to get alist from hash table and vice versa
@ 2017-10-09  0:25 Drew Adams
  2017-10-09 13:20 ` Michael Heerdegen
  2017-12-30 21:26 ` Philipp Stephani
  0 siblings, 2 replies; 23+ messages in thread
From: Drew Adams @ 2017-10-09  0:25 UTC (permalink / raw)
  To: 28753

Dunno whether functions like these might be useful.  I use something
similar.  If you think they're useful, consider adding them.

(cl-defun alist-to-hash-table (alist &optional use-last-p
                                     &key (test 'eql) weakness (size 65)
                                     (rehash-size 1.5) (rehash-threshold 0.8))
  "Create and return a hash table created from ALIST.
By default, if the same alist key is used in more than one alist entry
then only the first entry is used for the hash table.  Non-nil
USE-LAST-P means override this to use only the last entry for a given
key.

See `make-hash-table' for the keyword arguments you can use and their
default values."
  (let ((ht  (make-hash-table :test test :weakness weakness :size size
                              :rehash-size rehash-size :rehash-threshold rehash-threshold))
        key val)
    (dolist (key.val  alist)
      (setq key  (car key.val)
            val  (cdr key.val))
      (when (or use-last-p  (not (gethash key ht)))
        (puthash key val ht)))
    ht))

(defun hash-table-to-alist (hash-table)
  "Create and return an alist created from HASH-TABLE.
The order of alist entries is the same as the order of hash-table
entries (which normally is the order in which the entries were added
to the table)."
  (let ((al  ()))
    (maphash (lambda (key val) (push (cons key val) al)) hash-table)
    (nreverse al)))


In GNU Emacs 25.3.1 (x86_64-w64-mingw32)
 of 2017-09-26
Windowing system distributor `Microsoft Corp.', version 6.1.7601
Configured using:
 `configure --without-dbus --without-compress-install 'CFLAGS=-O2
 -static -g3' PKG_CONFIG_PATH=/mingw64/lib/pkgconfig'





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-10-09  0:25 bug#28753: 25.3; Functions to get alist from hash table and vice versa Drew Adams
@ 2017-10-09 13:20 ` Michael Heerdegen
  2017-10-09 14:11   ` Drew Adams
  2022-04-22 13:18   ` Lars Ingebrigtsen
  2017-12-30 21:26 ` Philipp Stephani
  1 sibling, 2 replies; 23+ messages in thread
From: Michael Heerdegen @ 2017-10-09 13:20 UTC (permalink / raw)
  To: Drew Adams; +Cc: 28753

Drew Adams <drew.adams@oracle.com> writes:

> Dunno whether functions like these might be useful.  I use something
> similar.  If you think they're useful, consider adding them.

I think something very similar is provided by map.el: `map-into'.


Michael.





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-10-09 13:20 ` Michael Heerdegen
@ 2017-10-09 14:11   ` Drew Adams
  2017-10-11 16:42     ` Drew Adams
  2022-04-22 13:18   ` Lars Ingebrigtsen
  1 sibling, 1 reply; 23+ messages in thread
From: Drew Adams @ 2017-10-09 14:11 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 28753

> > Dunno whether functions like these might be useful.  I use something
> > similar.  If you think they're useful, consider adding them.
> 
> I think something very similar is provided by map.el: `map-into'.

Good to know.  Thx.





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-10-09 14:11   ` Drew Adams
@ 2017-10-11 16:42     ` Drew Adams
       [not found]       ` <87wp4038m0.fsf@web.de>
  2017-10-12 13:30       ` Nicolas Petton
  0 siblings, 2 replies; 23+ messages in thread
From: Drew Adams @ 2017-10-11 16:42 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 28753

> > > Dunno whether functions like these might be useful.  I use something
> > > similar.  If you think they're useful, consider adding them.
> >
> > I think something very similar is provided by map.el: `map-into'.
> 
> Good to know.  Thx.

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.

`map-into' should probably accept additional, keyword args,
which would be passed to `map--into-hash-table'.

I imagine that `map-into' is intended to be extended to
more than alists and hash tables eventually.  Otherwise,
dedicated functions for those two types, such as what I
suggested, would make as much (or more) sense.

Whether or not it will be so extended, it would be good
for `map-into' to accept additional args that would be
passed to the appropriate type-conversion helper functions.

If we just allowed an &rest ARGS parameter, that would
handle any types that might want to deal with additional
args.  But that would be less convenient than using
keyword args for a hash table.

We could I guess just pass ARGS along but define the
helper function (e.g. `map--into-hash-table') using
`cl-defun' with appropriate keyword args.  IOW, at the
`map-into' level nothing would be specified about ARGS,
but each conversion helper could define what kinds of
args it accepts.

(There's also `&allow-other-keys', but probably it
doesn't make much sense for `map-into' to define any
keyword args.)

In that case, the helper function should not be
"internal", and the use of `make-hash-table' keyword
args should be mentioned in its doc string.

Although simple lookup in an Elisp alist typically
uses only `assoc' or `assq' (or `rassoc' or `rassq'),
a program that _uses_ an alist might well make use
of a different equality test for its elements.  It
need not be limited to testing membership using
`assoc' or `assq'.

So while the alist to be converted to a hash table
might not, itself, have any fancy notion of a :test
function, the appropriate "equivalent" hash table in
some context might well need to define a particular
:test.

This is why it makes sense to allow conversion to a
hash table to give programmers an ability to specify
:test (and other hash-table features).

Note too that in Common Lisp `assoc' takes a :test arg.
`map-into' is designed for alists that use `cl-assoc'
as much as for those that use `assoc'.  Unlike a hash
table, however, an alist doesn't itself record ir
require any particular :test function, so `map-into'
can't transfer a hash-table :test to an alist that it
produces from a hash table.  So the existing `map-into'
for conversion to an alist is good enough.





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
       [not found]       ` <87wp4038m0.fsf@web.de>
@ 2017-10-12 13:27         ` Nicolas Petton
  2017-10-12 13:46           ` Michael Heerdegen
                             ` (2 more replies)
  0 siblings, 3 replies; 23+ messages in thread
From: Nicolas Petton @ 2017-10-12 13:27 UTC (permalink / raw)
  To: Michael Heerdegen, Drew Adams; +Cc: 28753

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

Michael Heerdegen <michael_heerdegen@web.de> 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))'.

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

> Maybe Nicolas, maintainer of the library, wants to kick in (CC'ing
> him).

No you didn't :-P.

Nico

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

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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-10-11 16:42     ` Drew Adams
       [not found]       ` <87wp4038m0.fsf@web.de>
@ 2017-10-12 13:30       ` Nicolas Petton
  1 sibling, 0 replies; 23+ messages in thread
From: Nicolas Petton @ 2017-10-12 13:30 UTC (permalink / raw)
  To: Drew Adams, Michael Heerdegen; +Cc: 28753

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

Drew Adams <drew.adams@oracle.com> writes:

> I imagine that `map-into' is intended to be extended to
> more than alists and hash tables eventually.

Yes, the same way `seq.el' can be extended (using generic functions).  I
haven't yet found the time to do it though, it's a pretty big
refactoring of the library.

Cheers,
Nico

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

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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-10-12 13:27         ` Nicolas Petton
@ 2017-10-12 13:46           ` Michael Heerdegen
  2017-10-12 14:36           ` Drew Adams
  2017-10-12 15:56           ` Noam Postavsky
  2 siblings, 0 replies; 23+ messages in thread
From: Michael Heerdegen @ 2017-10-12 13:46 UTC (permalink / raw)
  To: Nicolas Petton; +Cc: 28753

Nicolas Petton <nicolas@petton.fr> writes:

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

If you don't like it, then maybe `map-into' is a misconception, and we
should use different functions as Drew suggested instead.


BTW, when I looked at map.el, i noticed that we have `map-some' and
`map-every-p'.  Is there a reason for the different naming style (one
has -p, the other one has not)?


Michael.





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-10-12 13:27         ` Nicolas Petton
  2017-10-12 13:46           ` Michael Heerdegen
@ 2017-10-12 14:36           ` Drew Adams
  2017-11-06 16:19             ` Drew Adams
  2017-10-12 15:56           ` Noam Postavsky
  2 siblings, 1 reply; 23+ messages in thread
From: Drew Adams @ 2017-10-12 14:36 UTC (permalink / raw)
  To: Nicolas Petton, Michael Heerdegen; +Cc: 28753

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

> Michael Heerdegen <michael_heerdegen@web.de> 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.)





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-10-12 13:27         ` Nicolas Petton
  2017-10-12 13:46           ` Michael Heerdegen
  2017-10-12 14:36           ` Drew Adams
@ 2017-10-12 15:56           ` Noam Postavsky
  2 siblings, 0 replies; 23+ messages in thread
From: Noam Postavsky @ 2017-10-12 15:56 UTC (permalink / raw)
  To: Nicolas Petton; +Cc: Michael Heerdegen, 28753

On Thu, Oct 12, 2017 at 9:27 AM, Nicolas Petton <nicolas@petton.fr> wrote:
> Michael Heerdegen <michael_heerdegen@web.de> 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))'.
>
> However, `keywords' would be ignored when converting to an alist.  I'm
> not sure I like it.

What about just receiving the hash-table as a parameter:

(defun map-into (map type)
  "Convert the map MAP into a map of type TYPE.

TYPE can be one of the following symbols: `list', `hash-table'; or it
can be a hash-table to use.
MAP can be a list, hash-table or array."
  (pcase type
    (`list (map-pairs map))
    (`hash-table (map--into-hash-table map))
    ((pred hash-tablep) (map--into-existing-hash-table map type)
    (_ (error "Not a map type name: %S" type)))))

(defun map--into-existing-hash-table (map hash-table)
  ...)





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-10-12 14:36           ` Drew Adams
@ 2017-11-06 16:19             ` Drew Adams
  2017-11-07  0:46               ` Noam Postavsky
  0 siblings, 1 reply; 23+ messages in thread
From: Drew Adams @ 2017-11-06 16:19 UTC (permalink / raw)
  To: Nicolas Petton, Michael Heerdegen; +Cc: 28753

> 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.)

Any news on this?  There is no general, abstract
solution proposed, so far, to meet the needs met
by the specific alist <-> hash-table code I sent.

Should we not add that code or similar to Emacs?





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-11-06 16:19             ` Drew Adams
@ 2017-11-07  0:46               ` Noam Postavsky
  2017-11-07  2:24                 ` Drew Adams
  2017-11-07 13:28                 ` Michael Heerdegen
  0 siblings, 2 replies; 23+ messages in thread
From: Noam Postavsky @ 2017-11-07  0:46 UTC (permalink / raw)
  To: Drew Adams; +Cc: Michael Heerdegen, Nicolas Petton, 28753

Drew Adams <drew.adams@oracle.com> writes:

> Any news on this?  There is no general, abstract
> solution proposed, so far, to meet the needs met
> by the specific alist <-> hash-table code I sent.

Did you have any comments for my proposal in #29?

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=28753#29





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-11-07  0:46               ` Noam Postavsky
@ 2017-11-07  2:24                 ` Drew Adams
  2017-11-07  2:51                   ` Noam Postavsky
  2017-11-07 13:28                 ` Michael Heerdegen
  1 sibling, 1 reply; 23+ messages in thread
From: Drew Adams @ 2017-11-07  2:24 UTC (permalink / raw)
  To: Noam Postavsky; +Cc: Michael Heerdegen, Nicolas Petton, 28753

> > Any news on this?  There is no general, abstract
> > solution proposed, so far, to meet the needs met
> > by the specific alist <-> hash-table code I sent.
> 
> Did you have any comments for my proposal in #29?
> 
> https://urldefense.proofpoint.com/v2/url?u=https-
> 3A__debbugs.gnu.org_cgi_bugreport.cgi-3Fbug-3D28753-
> 2329&d=DwIBAg&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=kI3P6ljGv6CTH
> IKju0jqInF6AOwMCYRDQUmqX22rJ98&m=2PshIV1HLbvhys07NQMj0gaYobwyLDaPs6zHJYzxwD
> Y&s=30GvxP6lcqjxDwNDmk0njIsHZRQE1V1VGEA4JNZLf60&e=

I don't see a complete proposal there.

Your solution is apparently to punt, telling users to
first create the hash table they need and then call a
function that injects the alist key+value entries into
that existing table.

I think that's less clear than the function I provided.

And in particular it does not provide a USE-LAST
possibility.  Does it?  Would you be adding an optional
such arg to `map--into-existing-hash-table', to handle
this?  Would the default behavior of that function use
only the first entry with a given key, as is typical for
an alist?

If you show how to achieve all of what this definition
achieves, then I guess it would be OK - provided its doc
makes it just as clear as is clear for this function how
to get specific alist-to-hash-table mappings:

(cl-defun alist-to-hash-table (alist &optional use-last-p
                                     &key (test 'eql) weakness (size 65)
                                     (rehash-size 1.5) (rehash-threshold 0.8))
  "Create and return a hash table created from ALIST.
By default, if the same alist key is used in more than one alist entry
then only the first entry is used for the hash table.  Non-nil
USE-LAST-P means override this to use only the last entry for a given
key.

See `make-hash-table' for the keyword arguments you can use and their
default values."
  (let ((ht  (make-hash-table
               :test test :weakness weakness :size size
               :rehash-size rehash-size
               :rehash-threshold rehash-threshold))
        key val)
    (dolist (key.val  alist)
      (setq key  (car key.val)
            val  (cdr key.val))
      (when (or use-last-p  (not (gethash key ht)))
        (puthash key val ht)))
    ht))

Users shouldn't have to reinvent that by figuring out how
they might achieve its possibilities using `map-into'.

You would presumably start by defining
`map--into-existing-hash-table'.  (Why would that function
be "internal", BTW?)





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-11-07  2:24                 ` Drew Adams
@ 2017-11-07  2:51                   ` Noam Postavsky
  0 siblings, 0 replies; 23+ messages in thread
From: Noam Postavsky @ 2017-11-07  2:51 UTC (permalink / raw)
  To: Drew Adams; +Cc: Michael Heerdegen, Nicolas Petton, 28753

Drew Adams <drew.adams@oracle.com> writes:

>> Did you have any comments for my proposal in #29?
>
> I don't see a complete proposal there.

Correct, I wanted to get comments on the whether the approach was
acceptable before finishing it.

> Your solution is apparently to punt, telling users to
> first create the hash table they need and then call a
> function that injects the alist key+value entries into
> that existing table.

Yes, if they want a hash table with specific parameters.

> I think that's less clear than the function I provided.

I think it has the advantage that the conversion function doesn't have
to know about all the possible parameters involved in hash table
creation.  Therefore the interface is simpler.

> And in particular it does not provide a USE-LAST
> possibility.

No, but I think it could be easily added.

> You would presumably start by defining
> `map--into-existing-hash-table'.  (Why would that function
> be "internal", BTW?)

I meant it as a variant on `map--into-hash-table', so it uses the same
naming convention.





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-11-07  0:46               ` Noam Postavsky
  2017-11-07  2:24                 ` Drew Adams
@ 2017-11-07 13:28                 ` Michael Heerdegen
  2017-12-30 20:40                   ` Philipp Stephani
  1 sibling, 1 reply; 23+ messages in thread
From: Michael Heerdegen @ 2017-11-07 13:28 UTC (permalink / raw)
  To: Noam Postavsky; +Cc: Nicolas Petton, 28753

Noam Postavsky <npostavs@users.sourceforge.net> writes:

> Drew Adams <drew.adams@oracle.com> writes:
>
> > Any news on this?  There is no general, abstract
> > solution proposed, so far, to meet the needs met
> > by the specific alist <-> hash-table code I sent.
>
> Did you have any comments for my proposal in #29?

That's clever.

OTOH I'm not convinced that `map-into' is a good abstraction.  Every
goal type might have an individual set of useful parameters, especially
when we want to add support for other map types in the future.  Our
choice now to unite those in one conversion interface function might
result in quite a mess later.


Michael.





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-11-07 13:28                 ` Michael Heerdegen
@ 2017-12-30 20:40                   ` Philipp Stephani
  2017-12-30 21:08                     ` Drew Adams
  0 siblings, 1 reply; 23+ messages in thread
From: Philipp Stephani @ 2017-12-30 20:40 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: Nicolas Petton, 28753, Noam Postavsky

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

Michael Heerdegen <michael_heerdegen@web.de> schrieb am Di., 7. Nov. 2017
um 14:29 Uhr:

> Noam Postavsky <npostavs@users.sourceforge.net> writes:
>
> > Drew Adams <drew.adams@oracle.com> writes:
> >
> > > Any news on this?  There is no general, abstract
> > > solution proposed, so far, to meet the needs met
> > > by the specific alist <-> hash-table code I sent.
> >
> > Did you have any comments for my proposal in #29?
>
> That's clever.
>
> OTOH I'm not convinced that `map-into' is a good abstraction.  Every
> goal type might have an individual set of useful parameters, especially
> when we want to add support for other map types in the future.  Our
> choice now to unite those in one conversion interface function might
> result in quite a mess later.
>

I don't think a unified conversion interface is that important. The various
structures used for mappings are just too different. For example, alists
and plists aren't real types, they are only defined by their usage. Hash
tables, on the other hand, are real types, with a per-object comparison
function, a non-nil empty value, etc. These two kinds of objects are just
too different to treat uniformly. Also, in most cases it is statically
known which kinds of objects are involved, so a generic function that
dynamically selects on the type of object isn't that useful. How about
adding some simple conversion functions to subr.el such as
(defun alist-to-hashtable (alist &rest keys) ...)
(defun hashtable-to-alist (hashtable) ...)

[-- Attachment #2: Type: text/html, Size: 2055 bytes --]

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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-12-30 20:40                   ` Philipp Stephani
@ 2017-12-30 21:08                     ` Drew Adams
  2017-12-30 21:15                       ` Philipp Stephani
  0 siblings, 1 reply; 23+ messages in thread
From: Drew Adams @ 2017-12-30 21:08 UTC (permalink / raw)
  To: Philipp Stephani, Michael Heerdegen; +Cc: Nicolas Petton, 28753, Noam Postavsky

> I don't think a unified conversion interface is that important.
> The various structures used for mappings are just too different.
> For example, alists and plists aren't real types, they are only
> defined by their usage. Hash tables, on the other hand, are real
> types, with a per-object comparison function, a non-nil empty
> value, etc. These two kinds of objects are just too different
> to treat uniformly. Also, in most cases it is statically known
> which kinds of objects are involved, so a generic function that
> dynamically selects on the type of object isn't that useful.
>
> How about adding some simple conversion functions to subr.el such as
> 
> (defun alist-to-hashtable (alist &rest keys) ...) 
> (defun hashtable-to-alist (hashtable) ...)

Which brings us back to the very first msg of this thread -
the bug report.  Please see the code I proposed there.

And note the differences from the signature you show for
`alist-to-hashtable'.  I think those differences are
important and your signature is not satisfactory.





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-12-30 21:08                     ` Drew Adams
@ 2017-12-30 21:15                       ` Philipp Stephani
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Stephani @ 2017-12-30 21:15 UTC (permalink / raw)
  To: Drew Adams; +Cc: Michael Heerdegen, Nicolas Petton, 28753, Noam Postavsky

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

Drew Adams <drew.adams@oracle.com> schrieb am Sa., 30. Dez. 2017 um
22:08 Uhr:

> > I don't think a unified conversion interface is that important.
> > The various structures used for mappings are just too different.
> > For example, alists and plists aren't real types, they are only
> > defined by their usage. Hash tables, on the other hand, are real
> > types, with a per-object comparison function, a non-nil empty
> > value, etc. These two kinds of objects are just too different
> > to treat uniformly. Also, in most cases it is statically known
> > which kinds of objects are involved, so a generic function that
> > dynamically selects on the type of object isn't that useful.
> >
> > How about adding some simple conversion functions to subr.el such as
> >
> > (defun alist-to-hashtable (alist &rest keys) ...)
> > (defun hashtable-to-alist (hashtable) ...)
>
> Which brings us back to the very first msg of this thread -
> the bug report.  Please see the code I proposed there.
>

OK thanks. I think your definitions would be a useful addition to subr.el.


>
> And note the differences from the signature you show for
> `alist-to-hashtable'.  I think those differences are
> important and your signature is not satisfactory.
>

NB, I have the `&rest keys` in my arglist, with similar intentions as your
definition.

[-- Attachment #2: Type: text/html, Size: 1882 bytes --]

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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-10-09  0:25 bug#28753: 25.3; Functions to get alist from hash table and vice versa Drew Adams
  2017-10-09 13:20 ` Michael Heerdegen
@ 2017-12-30 21:26 ` Philipp Stephani
  2017-12-31  0:01   ` Drew Adams
  1 sibling, 1 reply; 23+ messages in thread
From: Philipp Stephani @ 2017-12-30 21:26 UTC (permalink / raw)
  To: Drew Adams; +Cc: 28753

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

Drew Adams <drew.adams@oracle.com> schrieb am Mo., 9. Okt. 2017 um
02:27 Uhr:

> Dunno whether functions like these might be useful.  I use something
> similar.  If you think they're useful, consider adding them.
>

I think both are useful.


>
> (cl-defun alist-to-hash-table (alist &optional use-last-p
>                                      &key (test 'eql) weakness (size 65)
>                                      (rehash-size 1.5) (rehash-threshold
> 0.8))
>   "Create and return a hash table created from ALIST.
> By default, if the same alist key is used in more than one alist entry
> then only the first entry is used for the hash table.  Non-nil
> USE-LAST-P means override this to use only the last entry for a given
> key.
>
> See `make-hash-table' for the keyword arguments you can use and their
> default values."
>   (let ((ht  (make-hash-table :test test :weakness weakness :size size
>                               :rehash-size rehash-size :rehash-threshold
> rehash-threshold))
>         key val)
>     (dolist (key.val  alist)
>       (setq key  (car key.val)
>             val  (cdr key.val))
>       (when (or use-last-p  (not (gethash key ht)))
>

This doesn't work if the value is nil. You need to use an uninterned symbol
or some other unique object, e.g.
(eq (gethash key ht #1='#:void) #1#)


>         (puthash key val ht)))
>     ht))
>

I'd personally make use-last-p another keyword argument, though.


>
> (defun hash-table-to-alist (hash-table)
>   "Create and return an alist created from HASH-TABLE.
> The order of alist entries is the same as the order of hash-table
> entries (which normally is the order in which the entries were added
> to the table)."
>   (let ((al  ()))
>     (maphash (lambda (key val) (push (cons key val) al)) hash-table)
>     (nreverse al)))
>
>
Hmm, is the order guaranteed? I haven't found anything in the Emacs Lisp
manual about this, so maybe just leave out the parenthetical remark or say
that the order is unspecified?

[-- Attachment #2: Type: text/html, Size: 2967 bytes --]

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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-12-30 21:26 ` Philipp Stephani
@ 2017-12-31  0:01   ` Drew Adams
  2018-03-04 19:17     ` Philipp Stephani
  0 siblings, 1 reply; 23+ messages in thread
From: Drew Adams @ 2017-12-31  0:01 UTC (permalink / raw)
  To: Philipp Stephani; +Cc: 28753

> > (when (or use-last-p  (not (gethash key ht)))
>
> This doesn't work if the value is nil. 

I guess you mean that it doesn't DTRT if the cdr of an alist
entry is `nil' - e.g. (foo) aka (foo . nil).

> You need to use an uninterned symbol or some other unique object, e.g.
> (eq (gethash key ht #1='#:void) #1#)

OK.  Dunno which is clearer or whether there is some
performance difference, but I would probably just bind
a var to a unique cons, e.g. (cons 1 1), outside the
`dolist'.  E.g.:

(let ((ht (make-hash-table :test test :weakness weakness
                           :size size :rehash-size rehash-size
                           :rehash-threshold rehash-threshold))
      (uniq-cons (cons 1 1))
      key val)
  (dolist (key.val  alist)
    (setq key  (car key.val)
          val  (cdr key.val))
    (when (or use-last-p  (not (eq (gethash key ht uniq-cons))))
      (puthash key val ht)))
  ht))

(With your approach of using an uninterned symbol, wouldn't
you want to do the same thing: bind a var to it outside the
`dolist', or would that make no real difference?)
 
> I'd personally make use-last-p another keyword argument, though.

I don't have a strong objection, but why?

> > (defun hash-table-to-alist (hash-table)
> >  "Create and return an alist created from HASH-TABLE.
> > The order of alist entries is the same as the order of hash-table
> > entries (which normally is the order in which the entries were added
> > to the table)."
> >  (let ((al  ()))
> >    (maphash (lambda (key val) (push (cons key val) al)) hash-table)
> >    (nreverse al)))
>
> Hmm, is the order guaranteed? I haven't found anything in the
> Emacs Lisp manual about this, so maybe just leave out the
> parenthetical remark or say that the order is unspecified?

Good question!  But it's not just the parenthetical part.
If we couldn't say anything about the traversal order of
`maphash' then the whole sentence would need to go.

FWIW, I think it's pretty important.  Order is quite
important for alists, generally.

Is there some reason we should not be able to count on
this `maphash' behavior?

That's the behavior I saw, AFAICT, but I didn't test much.

I don't know whether it is guaranteed, but this use case
involving conversion to alist looks like a good argument for
some guarantee wrt order.

I see that in Common Lisp nothing is said about the traversal
by `maphash' over the hash table.  This is all that is said:

 "For each entry in hash-table, maphash calls function on two
  arguments: the key of the entry and the value of the entry;
  maphash then returns nil. If entries are added to or deleted
  from the hash table while a maphash is in progress, the results
  are unpredictable, with one exception: if the function calls
  remhash to remove the entry currently being processed by the
  function, or performs a setf of gethash on that entry to change
  the associated value, then those operations will have the
  intended effect."

  - https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node155.html

(Emacs doc should also probably say at least something about
what happens when entries are added/removed during a `maphash'
application.)

If Emacs decides not to guarantee the order seen now, which I
think is the order I described in the doc string above, then we
would need to remove that sentence.  That would be too bad for
users of function `hash-table-to-alist', but at least they
would, at least so far (and AFAICT), get that behavior, which
is likely to be expected by them even if it is not documented.

Another possibility (?): Allow _optional_ creation of a hash
table that does preserve such ordering (even if just by
recording order of entry and making that available to `maphash').
E.g., add a keyword arg for `make-hash-table' that does just that.

Even if it turned out that this consistently or occasionally
detracted from performance, it could be a useful option.
Conversion to an alist would be a case in point.





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-12-31  0:01   ` Drew Adams
@ 2018-03-04 19:17     ` Philipp Stephani
  2018-03-05  0:01       ` Drew Adams
  0 siblings, 1 reply; 23+ messages in thread
From: Philipp Stephani @ 2018-03-04 19:17 UTC (permalink / raw)
  To: Drew Adams; +Cc: 28753

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

Drew Adams <drew.adams@oracle.com> schrieb am So., 31. Dez. 2017 um
01:01 Uhr:

> > > (when (or use-last-p  (not (gethash key ht)))
> >
> > This doesn't work if the value is nil.
>
> I guess you mean that it doesn't DTRT if the cdr of an alist
> entry is `nil' - e.g. (foo) aka (foo . nil).
>

Yes.


>
> > You need to use an uninterned symbol or some other unique object, e.g.
> > (eq (gethash key ht #1='#:void) #1#)
>
> OK.  Dunno which is clearer or whether there is some
> performance difference, but I would probably just bind
> a var to a unique cons, e.g. (cons 1 1), outside the
> `dolist'.  E.g.:
>
> (let ((ht (make-hash-table :test test :weakness weakness
>                            :size size :rehash-size rehash-size
>                            :rehash-threshold rehash-threshold))
>       (uniq-cons (cons 1 1))
>       key val)
>   (dolist (key.val  alist)
>     (setq key  (car key.val)
>           val  (cdr key.val))
>     (when (or use-last-p  (not (eq (gethash key ht uniq-cons))))
>       (puthash key val ht)))
>   ht))
>
> (With your approach of using an uninterned symbol, wouldn't
> you want to do the same thing: bind a var to it outside the
> `dolist', or would that make no real difference?)
>

It's no real difference. I just proposed the shortest way that works.


>
> > I'd personally make use-last-p another keyword argument, though.
>
> I don't have a strong objection, but why?
>

Especially for booleans it's much clearer if the parameter name is repeated
on the call site.


>
> > > (defun hash-table-to-alist (hash-table)
> > >  "Create and return an alist created from HASH-TABLE.
> > > The order of alist entries is the same as the order of hash-table
> > > entries (which normally is the order in which the entries were added
> > > to the table)."
> > >  (let ((al  ()))
> > >    (maphash (lambda (key val) (push (cons key val) al)) hash-table)
> > >    (nreverse al)))
> >
> > Hmm, is the order guaranteed? I haven't found anything in the
> > Emacs Lisp manual about this, so maybe just leave out the
> > parenthetical remark or say that the order is unspecified?
>
> Good question!  But it's not just the parenthetical part.
> If we couldn't say anything about the traversal order of
> `maphash' then the whole sentence would need to go.
>
> FWIW, I think it's pretty important.  Order is quite
> important for alists, generally.
>
> Is there some reason we should not be able to count on
> this `maphash' behavior?
>

Order in hashtables is not guaranteed. If anything relies on the order,
it's buggy.


>
> That's the behavior I saw, AFAICT, but I didn't test much.
>
> I don't know whether it is guaranteed, but this use case
> involving conversion to alist looks like a good argument for
> some guarantee wrt order.
>

No. Ordering guarantees require additional complexity and overhead, and are
almost never needed.


>
> I see that in Common Lisp nothing is said about the traversal
> by `maphash' over the hash table.  This is all that is said:
>
>  "For each entry in hash-table, maphash calls function on two
>   arguments: the key of the entry and the value of the entry;
>   maphash then returns nil. If entries are added to or deleted
>   from the hash table while a maphash is in progress, the results
>   are unpredictable, with one exception: if the function calls
>   remhash to remove the entry currently being processed by the
>   function, or performs a setf of gethash on that entry to change
>   the associated value, then those operations will have the
>   intended effect."
>
>   - https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node155.html
>
> (Emacs doc should also probably say at least something about
> what happens when entries are added/removed during a `maphash'
> application.)
>

Yes.


>
> If Emacs decides not to guarantee the order seen now, which I
> think is the order I described in the doc string above, then we
> would need to remove that sentence.  That would be too bad for
> users of function `hash-table-to-alist', but at least they
> would, at least so far (and AFAICT), get that behavior, which
> is likely to be expected by them even if it is not documented.
>
> Another possibility (?): Allow _optional_ creation of a hash
> table that does preserve such ordering (even if just by
> recording order of entry and making that available to `maphash').
> E.g., add a keyword arg for `make-hash-table' that does just that.
>
> Even if it turned out that this consistently or occasionally
> detracted from performance, it could be a useful option.
> Conversion to an alist would be a case in point.
>

I don't think it would be worth the additional complexity. Hash tables have
been available for ages in most programming languages, and almost never
does anybody ask for ordering guarantees. (For example, I have never seen
somebody using LinkedHashMap in Java.)
Even for alists, most of the time maintaining insertion order is an
irrelevant detail, most users care only about get/put/remove.

[-- Attachment #2: Type: text/html, Size: 6864 bytes --]

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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2018-03-04 19:17     ` Philipp Stephani
@ 2018-03-05  0:01       ` Drew Adams
  0 siblings, 0 replies; 23+ messages in thread
From: Drew Adams @ 2018-03-05  0:01 UTC (permalink / raw)
  To: Philipp Stephani; +Cc: 28753

>> Order is quite important for alists, generally.
>> Is there some reason we should not be able to count on
>> this `maphash' behavior?
>
> Order in hashtables is not guaranteed. If anything relies
> on the order, it's buggy.
 
Emacs hash tables don't fall from the sky.  They are not
shadows of some Platonic ideal.  This is a design choice.
Emacs Lisp can implement hash tables any way it wants.

>> I don't know whether it is guaranteed, but this use case
>> involving conversion to alist looks like a good argument
>> for some guarantee wrt order.
>
> No. Ordering guarantees require additional complexity and
> overhead, and are almost never needed.

Neither of those assertions has been demonstrated.  Feel
free to do so (how much overhead, how infrequent).

And infrequency of use is not, alone, a good reason not
to support something.  Infrequent does not mean unimportant.

>> Another possibility (?): Allow _optional_ creation of
>> a hash table that does preserve such ordering (even if
>> just by recording order of entry and making that
>> available to `maphash').  E.g., add a keyword arg for
>> `make-hash-table' that does just that.
>>
>> Even if it turned out that this consistently or occasionally
>> detracted from performance, it could be a useful option.
>> Conversion to an alist would be a case in point.
>
> I don't think it would be worth the additional complexity.

Why?  How much additional complexity?

> Hash tables have been available for ages in most programming
> languages, and almost never does anybody ask for ordering
> guarantees. (For example, I have never seen somebody using
> LinkedHashMap in Java.)

Emacs Lisp has lots of features that are not in "most
programming languages".  Propertized strings, for one
trivial example.

> Even for alists, most of the time maintaining insertion
> order is an irrelevant detail,

What time maintaining insertion order?  I can't imagine
what you mean by that.

> most users care only about get/put/remove.

Not demonstrated.  But even if it were true, it wouldn't
follow that Lisp alist order is unimportant.

Next you'll be suggesting that because lists are mutated
relatively infrequently it is unimportant to be able to
modify list structure or test cons equality with `eq'.
Or that because true lists are used most of the time we
might as well drop support of dotted lists.

----

Naive google search -

https://www.javaspecialists.eu/archive/Issue073.html

https://stackoverflow.com/questions/12998568/hashmap-vs-linkedhashmap-performance-in-iteration-over-values/12998878

https://stackoverflow.com/questions/26623129/when-to-use-linkedhashmap-over-hashmap-in-java





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2017-10-09 13:20 ` Michael Heerdegen
  2017-10-09 14:11   ` Drew Adams
@ 2022-04-22 13:18   ` Lars Ingebrigtsen
  2022-04-22 15:21     ` Drew Adams
  1 sibling, 1 reply; 23+ messages in thread
From: Lars Ingebrigtsen @ 2022-04-22 13:18 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 28753

Michael Heerdegen <michael_heerdegen@web.de> writes:

> I think something very similar is provided by map.el: `map-into'.

The discussion then went onto how to specify parameters to
make-hash-table, and I see that this is fixed now like this:

  (map-into '((1 . 3)) '(hash-table :test eql))

So there doesn't seem to be anything further to do here, and I'm
therefore closing this bug report.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no





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

* bug#28753: 25.3; Functions to get alist from hash table and vice versa
  2022-04-22 13:18   ` Lars Ingebrigtsen
@ 2022-04-22 15:21     ` Drew Adams
  0 siblings, 0 replies; 23+ messages in thread
From: Drew Adams @ 2022-04-22 15:21 UTC (permalink / raw)
  To: Lars Ingebrigtsen, Michael Heerdegen; +Cc: 28753@debbugs.gnu.org

> Michael Heerdegen <michael_heerdegen@web.de> writes:
> 
> > I think something very similar is provided by map.el: `map-into'.
> 
> The discussion then went onto how to specify parameters to
> make-hash-table, and I see that this is fixed now like this:
> 
>   (map-into '((1 . 3)) '(hash-table :test eql))
> 
> So there doesn't seem to be anything further to do here, and I'm
> therefore closing this bug report.

IOW, you ignored the entire thread.  The bug is NOT
about "how to specify parameters to make-hash-table".

Yet another won't-fix.





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

end of thread, other threads:[~2022-04-22 15:21 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-10-09  0:25 bug#28753: 25.3; Functions to get alist from hash table and vice versa Drew Adams
2017-10-09 13:20 ` Michael Heerdegen
2017-10-09 14:11   ` Drew Adams
2017-10-11 16:42     ` Drew Adams
     [not found]       ` <87wp4038m0.fsf@web.de>
2017-10-12 13:27         ` Nicolas Petton
2017-10-12 13:46           ` Michael Heerdegen
2017-10-12 14:36           ` Drew Adams
2017-11-06 16:19             ` Drew Adams
2017-11-07  0:46               ` Noam Postavsky
2017-11-07  2:24                 ` Drew Adams
2017-11-07  2:51                   ` Noam Postavsky
2017-11-07 13:28                 ` Michael Heerdegen
2017-12-30 20:40                   ` Philipp Stephani
2017-12-30 21:08                     ` Drew Adams
2017-12-30 21:15                       ` Philipp Stephani
2017-10-12 15:56           ` Noam Postavsky
2017-10-12 13:30       ` Nicolas Petton
2022-04-22 13:18   ` Lars Ingebrigtsen
2022-04-22 15:21     ` Drew Adams
2017-12-30 21:26 ` Philipp Stephani
2017-12-31  0:01   ` Drew Adams
2018-03-04 19:17     ` Philipp Stephani
2018-03-05  0:01       ` Drew Adams

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.