ruricolist / serapeum

Utilities beyond Alexandria
MIT License
420 stars 41 forks source link

Util suggestion: plist-p #99

Open Ambrevar opened 2 years ago

Ambrevar commented 2 years ago

It occurred to me a few times that I had to check whether an object is a valid plist. A plist-p helper would be nice here!

trivial-types has property-list-p but it accepts '(foo 1) which seems wrong to me.

Here is a fix:

(defun property-list-p (object)
  "XXX"
  (declare (optimize . #.*standard-optimize-qualities*))
  (typecase object
    (null t)
    (cons
     (loop
       (if (null object) 
           (return t)
           (let ((key (car object))
                 (next (cdr object)))
             (if (or (not (keywordp key))
                     (not (consp next)))
                 (return)
                 (setq object (cdr next)))))))))

Alternatively, shorter but less efficient I presume:

(defun plist-p (object)
  "Return non-nil if OBJECT is a plist."
  (and (listp object)
       (alex:proper-list-p object)
       (evenp (length object))
       (loop :for x :in object :by #'cddr
             :always (keywordp x))))

Thoughts?

ruricolist commented 2 years ago

I think the definition of "property list" includes any kind of symbol, not just keywords. After all you can define functions that take plain symbols as keywords (e.g. (defun fn (&key ((x x) nil)) x) which would be called as (fn 'x 1)). But I can also see the usefulness of being able to test for a property list that only allows keywords when that's what you expect.

Ambrevar commented 2 years ago

Good point!