Search code examples
common-lispplistkeyword-argument

Common Lisp remf indicator from plist not working


I have a function called use which accepts an arbitrary number of keyword arguments (as a plist) defined as below:

(defun use (&rest plist &key &allow-other-keys)
  ":href should be supplied, as it is the path/to/svg#id to be used."
  ;; Hole die Sachen die wichtig sind aus der PListe
    (let ((href (getf plist :href "#")))
      ;; Remove :href otherwise it appears in the plist again!
      (remf-indicators-from-plist '(:href) plist)
      (list plist href)
    ))

Here i extract some keyword arguments which are important and should be specified by the user, and then remove them from the plist of supplied keyword arguments with the remf-indicators-from-plist, so that i don't process them twice. remf-indicators-from-plist is defined below:

(defun remf-indicators-from-plist (indicators plist)
  "Removes indicators from the plist, 
to prevent callers from double-using them."
  (dolist (indicator indicators plist)
    (remf plist indicator)))

Now it seems to me if the keyword argument/s i want to extract in the use function and then remove, are indeed removed if they appear not as the first parameter to the function:

(use :x 34 :href 23 :c2 32)     ;((:X 34 :C2 32) 23)
(use :x 34 :c2 32 :href 23)     ;((:X 34 :C2 32) 23)

but not if it appears as the first parameter:

(use :href 23 :x 34 :c2 32)     ;((:HREF 23 :X 34 :C2 32) 23)

Why is this? And how can i implement it correctly?


Solution

  • The problem is in the function use, while remf-indicators-from-plist works correctly.

    The reason is that the function remf-indicators-from-plist returns the list modified (inside the dolist), but the value returned is discarded inside use. A possible solution is to change the definition of use for instance as:

    CL-USER> (defun use (&rest plist &key &allow-other-keys)
                ":href should be supplied, as it is the path/to/svg#id to be used."
                ;; Hole die Sachen die wichtig sind aus der PListe
              (let ((href (getf plist :href "#"))
                    (new-plist (remf-indicators-from-plist '(:href) plist)))
                (list new-plist href)))
    USE
    CL-USER> (use :x 34 :href 23 :c2 32)  
    ((:X 34 :C2 32) 23)
    CL-USER> (use :href 23 :x 34 :c2 32) 
    ((:X 34 :C2 32) 23)
    

    The fact that when :href is inside the plist everything apparently works, is due to the particular way side-effects are performed by certain operations in Common Lisp. A rule that help in managing side-effecting operations (a part from the fact that constant data should never be modified) is the following: always returns the structure that is modified, even if the modifications seems to happen “in place”.