P-List utilities

Utilities to manipulate plists, i.e. property lists, i.e. even-length lists where each even-indexed element is a key, and each following odd-indexed element is the associated value.

To use the bindings from this module:

(import :std/misc/plist)

plist?

(plist? plist) -> boolean

  plist := property list to check

Checks whether plist is a proper property list and returns a truth value (#t or #f). plist needs to be finite, circular lists are not supported.

A proper property list is a list of alternating keys and values of the following form: ((key1 value1 key2 value2 ...))

Examples:

> (plist? '(a 1 b 2 c 3 d 4))
#t

> (plist? ["uno" [1 2 3] "dos" [4 5 6] "tres" [7 8 9]])
#t

> (plist? '((a . 1) (b . 2)))
#t    ; key1 = (a . 1), value1 = (b . 2)

> (plist? '((a . 1) (b . 2) (c . 3)))
#f    ; key2 = (c . 3), but missing value2

> (plist? [])
#t    ; edge-case, just like (list? []) => #t

alist->plist

(alist->plist alist) -> plist | error

  alist := association list to transform

Transforms an association list ((k1 . v1) (k2 . v2) ...) into a property list (k1 v1 k2 v2 ...). alist needs to be finite, circular lists are not supported. Furthermore, an error is signaled when alist is an improper association list.

Examples:

> (alist->plist [[1 . 10] [2 . 20] [3 . 30]])
(1 10 2 20 3 30)

> (alist->plist [["fire" #\f] ["water" #\w] ["earth" #\e]])
("fire" (#\f) "water" (#\w) "earth" (#\e))

> (alist->plist '((1 2 3) (4 5 6) (7 8 9)))
(1 (2 3) 4 (5 6) 7 (8 9))

> (alist->plist '((a) (b) (c)))
(a () b () c ())

> (alist->plist [])
()

psetq!, psetv!, pset!, pgetq-set!, pgetv-set!, pget-set!

(psetq! plist key value)
(pgetq-set! key plist [default] value)

(psetv! plist key value)
(psetv-set! key plist [default] value)

(pset! plist key value)
(pset-set! key plist [default] value)

These functions complement the pgetq, pgetv, pget functions from the prelude, and enable the destructive update of a plist (property-list), i.e. a association list that follows the [key1 value1 key2 value2 ... keyN valueN], by either modifying in-place an entry with the given key, or adding a new entry. The functions all return #!void.

Just like the plist getter functions, these functions are distinguished by which equality predicate is used to compare keywords: eq?, eqv? or equal? respectively. eq? is best for keys being symbols and keywords, eqv? for numbers, and equal? for strings or lists, etc.

Each function comes in two variant: the first one accepts (psetq! plist key value) as the order of arguments, whereas the second one follows the convention so that you can use (set! (pgetq key plist) value) or (set! (pgetq key plist default) value). In the last case, the default value is ignored, but accepted for compatibility with macros that use both a getter then a setter for a same expression.

Last but not least, destructive operations are not allowed on an empty plist. If you use pset! or its friends, you have to ensure your plists are never empty. For instance you may keep a dummy key at the end of your plist that never gets removed. If this constraint is not acceptable, you may instead storing your plist in a variable (or struct field), use the pure pset operation, and update the variable (or struct field) with the result of it.

Examples:

(let (p ['a 1 'b 2]) (psetq! p 'a 3) p)
> ['a 3 'b 2]

(let (p ['a 1 'b 2]) (psetq! p 'c 3) p)
> ['c 3 'a 1 'b 4]

premq!, premv!, prem!

(premq! key plist)
(premv! key plist)
(prem! key plist)

These functions destructively modify a plist (property-list) to remove the entry for a given key. Just like the plist getter and setter functions, these functions are distinguished by which equality predicate is used to compare keywords: eq?, eqv? or equal? respectively. See pset! above about plists. The functions all return #!void.

It is not allowed to destructively remove the last entry in a plist. If you use prem! or its friends, you have to ensure your plists are never empty. For instance you may keep a dummy key at the end of your plist that never gets removed. If this constraint is not acceptable, you may instead storing your plist in a variable (or struct field), use the pure prem operation, and update the variable (or struct field) with the result of it.

Examples:

(let (p ['a 1 'b 2]) (premq! 'a p) p)
> ['b 2]

(let (p ['a 1 'b 2]) (prem! 'c p) p)
> ['a 1 'b 2]