4

Q: Can hash-table entries have property lists and, if no, what are the idiomatic alternatives?

As the manual explains, symbols can record miscellaneous information via symbol properties. I'd like to know if this applies to hash tables with symbols as keys.

Here's a toy example:

(setf hash-1 (make-hash-table)
      hash-2 (make-hash-table))

(puthash :key-1 "val-1" hash-1)
(puthash :key-2 "val-2" hash-2)

Can I attach symbol properties to :key-1 -- and, particularly, different properties for the two hash tables -- for later inspection? I'm guessing no, as neither gethash nor puthash seem to offer facilities for manipulating the key -- only the value. (See also this thread.)

If that's correct, what is the idiomatic alternative to symbol properties for hash-table entries?

I could, for example, assign an alist as the value of a hash entry:

(puthash :key-2 '((:val "val-3") (:prop prop-1)) hash-1)
(puthash :key-2 '((:val "val-4") (:prop prop-2)) hash-2)

And then each entry has a value list and a "property" list:

(assq :val  (gethash :key-2 hash-1))    ; => (:val "val-3")
(assq :prop (gethash :key-2 hash-1))    ; => (:prop prop-1)
(assq :val  (gethash :key-2 hash-2))    ; => (:val "val-4")
(assq :prop (gethash :key-2 hash-2))    ; => (:prop prop-2)

That seems a little convoluted, however. Is there a cleaner way to do this?

Dan
  • 32,584
  • 6
  • 98
  • 168
  • Wrt diff prop vals for the same symbol: No, but the value can itself be a symbol whose value as a variable depends on something, or it can be a function, whose return value depends on something. IOW, your "*later inspection*" can presumably get the property value and use it to get the real info you need, which can depend on whatever it needs to. – Drew Feb 28 '15 at 19:40
  • You can also use different symbols (with different properties) that have the same name. To do this, you intern the names in [different obarrays](http://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Symbols.html). – Drew May 06 '15 at 15:27

2 Answers2

7

As the manual explains, symbols can record miscellaneous information via symbol properties. I'd like to know if this applies to hash tables with symbols as keys.

Yes, it applies to any usage of symbols.

Can I attach symbol properties to :key-1

Yes.

(put :key-1 'hi "yes")
(get :key-1 'hi) ;; => "yes"

-- and, particularly, different properties for the two hash tables -- for later inspection?

You mean if the same key can have different symbols properties on different tables?
No. Because the symbol properties are part of the symbol, not of the table.

Furthermore, and this might be the key point to understand, interned symbols are unique in elisp. This means every appearance of :key-1 will always be the exact same lisp object (not just different objects with same structure). This, in turn, means that ':key-1 is eq to (intern ":key-1"), and all the following lines will return the same thing.

(get (intern ":key-1") 'hi)
(get ':key-1 'hi)
(get :key-1 'hi)

As a bonus, elisp binds keywords to themselves, so (eval :key-1) or (symbol-value :key-1) or even :key-1 all return :key-1. That's why you can use keywords without quoting them, why the third line above works, and why (eq :key ':key).

If that's correct, what is the idiomatic alternative to symbol properties for hash-table entries?

Symbol properties are for symbols. If you want to store data in a hash table that's associated to a key, why not use the hash table?

I could, for example, assign an alist as the value of a hash entry:
And then each entry has a value list and a "property" list:
That seems a little convoluted, however. Is there a cleaner way to do this?

Why convoluted?
IIUC, these property lists you're trying to store are really just extra values for your key.

When you go from “storing a value at a given key” to “storing multiple values at a given key” you need to add at least one level of specification to your get function. Which is to say, you need to increase the complexity.

If you just think the actual assq combo feels cumbersome, you could use a cl-struct as the value and retrieve fields with accessors. You can also use a property-list as the value (instead of the alist you have there) and access fields with plist-get. Finally, you can just use a separate key for the properties, something like :key-1--properties (though the viability of this will vary).

Malabarba
  • 22,878
  • 6
  • 78
  • 163
3

These are just two unrelated mechanisms. A hash-table is, in principle, a two-compartments structure. A tree-ish compartment for storing keys and an array-ish structure for storing values. Keys are stored in "buckets" where which key goes into what bucket and whether to add a new bucket for the new key is decided by a hash function applied to the key. The position in the bucket is later used to map the key to the value in the array-ish structure.

Symbol's properties are a kind of global hash-table, where there's a list reserved for each symbol. This list stores the values, which are that symbol's properties.

So, if you put a property in the symbol's property list that has no effect on that symbol being a key in any hash-table, conversely, whether you add a property to a symbol has no effect on that symbol being in a hash-table.

Perhaps it will help if I also mentioned that hash-table keys need not be symbols? Anything that can be hashed can be a hash-table key. So, lists, for example, can be keys too.

(defvar *test-table* (make-hash-table :test 'equal))
;; *test-table*
(setf (gethash (list 1 2 3) *test-table*) (list 4 5 6))
;; (4 5 6)
(gethash (list 1 2 3) *test-table*)
;; (4 5 6)

note that the list is fresh, meaning that, in principle, you can make two different symbols point to the same value in the same hash-table.

I believe that you could also design your own hashing and equality functions in order to control the behavior of the keys in the hash-table, but I would advise against doing that: it requires a good deal of understanding of the implementation of this data-structure. Probably, what you want, can be accomplished with more common techniques.

wvxvw
  • 11,222
  • 2
  • 30
  • 55