Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Namespaces and vars by

I'm taxed with porting a mess of old Lisp code into the 21st century.
I came across Clojure and think maybe it might be the avenue of best approach.
I am an absolute newbie to Clojure.

The Lisp code makes extensive use of symbol property lists.
Do Clojure symbols have property lists? Or something that "works" as a property list?

In Lisp, I could say:

(put 'fly 'verb 'transitive) ; The Verb property of "fly" is "transitive".
--> 'transitive
(put 'fly 'noun '(a buzzing little bug)) ; The Noun property of "fly" is "a buzzing little bug"
--> (a buzzing little bug)
(get 'fly 'verb) ; Show me the Verb property of "fly"
--> transitive
(symbol-plist 'fly) ; Show me all the properties of "fly"
--> (verb transitive noun (a buzzing little bug))

Does Clojure support symbol property-lists?

2 Answers

+2 votes
by

Clojure allow you to attach metadata (map of arbitrary info) to symbols and other objects (collections, vars, etc), which is probably a similar facility. In Clojure metadata, you could attach the verb and noun as metadata to a symbol (using keywords as keys is more idiomatic but anything is ok). I think the major difference is you'll have immutable vals/metadata instead of mutable.

(def f (with-meta 'fly {'verb 'transitive, 'noun '(a buzzing little bug)}))
(get (meta f) 'verb)
;;=> transitive
(meta f)
;;=> {verb transitive noun (a buzzing little bug)}

Also see:

https://clojure.org/reference/metadata

+1 vote
by

Well, yes. The data structure of property list is essentially a nested map (at least from what I can deduce from your example, i'm not really familiar with the original property lists). Hash-maps are built in i Clojure and very easy to work with. They can take any value as a key and value.

What differs more is that the example you give is mutable. This is certainly doable using atoms, outlined below, but a major thing with Clojure
is that it is syntactically cluncky to do things mutable. However, here's something working as you outlined:

(def db (atom {}))

(defn put [word prop value]
  (swap db assoc-in [word prop] value))

(defn get' [word prop]
  (get-in @db [word prop]))

(defn symbol-plist [word]
  (keys (get-in @db [word])))

Another thing is that people often use :keywords rather than 'symbols because symbols tend to try to resolve in annoying ways if not quoted. The same thing goes with lists. In litteral data-structures, it is more common to have collections as vectors (or sets).

And the data you construct in you example could be represented with

{'fly {'noun '(a buzzing little bug)
       'verb 'transitive}}
  

This data structure is immutable. symbol-plist is equal to (keys (get the-structure 'fly)), get is (get (get ... 'fly) 'verb) or (get-in ... ['fly 'verb]).

Put is (assoc-in ... ['fly 'adjective] 'nah-not-really)

But you have to save the reference, either in a closure during the execution, like

(let [db {'fly {'noun '(a buzzing little bug) 'verb 'transitive}}
      db (assoc-in db ['cat 'noun] '(a little furry animal))
      ...]
    db)

or in an atom as outlined in the first code block.

The idiomatic way is to use keywords (not symbols) for property values, but nothing prevents you from using symbols.

...