3

I'm trying to do some processing of values passed in a plist. Processing is quite similar: get a value, convert it to number if necessary, check if within bounds and if yes, return a string with that number, properly formatted.

Thus, I decided to create a list of actions, and then map through them, picking up non-nil values:

(defun knl--initialize-numerics (info)
  "Makes a string containing initialization of numerical parameters"
  ;; logic is: convert to number, if satisfies condition print it, skip otherwise
  (let* ((num-inits '((:knl-width 'identity (-partial '> 0) "%d")
                      (:knl-height 'identity (-partial '> 0) "%d")
                      (:knl-margin 'string-to-number (-partial '>= 0) "%.2f")
                      (:knl-min-scale 'string-to-number (-partial '> 0) "%.2f")
                      (:knl-max-scale 'string-to-number (-partial '> 0) "%.2f"))))
    (--map
     ;; function
     (-when-let* (((sym conv cond frm) it)
                  (oval (plist-get info sym))
                  (val (funcall conv oval))
                  (matches? (funcall cond val))
                  (name (s-lower-camel-case (s-chop-prefix ":knl-" (symbol-name sym))))
                  (formatted (format frm val)))
                  )
       val
       )
     ;; data
     num-inits)))

The above code fails at (val (funcall conv oval)) in ielm when I try to execute the following:

ELISP> (knl--initialize-numerics '(:knl-width 10 :knl-height 0 :knl-margin 0))
*** Eval error ***  Invalid function: (quote identity)

I was trying the same with a simple function, and it works:

ELISP> (let ((conv 'identity) (val 5)) (funcall conv val ))
5 (#o5, #x5, ?\C-e)

Why doesn't it then work in a more complex example? Is that because of some dash.el magic?

1 Answers1

10

There seems to be a fair bit of confusion how quoting works. In Lisp, symbols and lists fulfill a dual meaning, depending on whether they're quoted or not:

  • Unquoted symbol: Evaluate the symbol (by looking up its value in the current environment)
  • Quoted symbol: Do not evaluate the symbol (by returning the symbol)
  • Unquoted list: Evaluate the function (by applying its first item to the remaining items)
  • Quoted list: Do not evaluate the list (by returning the list as is)

The last case is particularly important as the quoting applies to everything inside the list. '(x y z) evaluates to (x y z), the symbols inside the list are not evaluated. Therefore, '(x 'y z) would evaluate to (x (quote y) z) which is most certainly not what one would want.

Sometimes you'll want to selectively evaluate parts of a quoted list. The hard way is to construct the list manually from quoted and not quoted symbols, the easier way is to use the backquote and unquote mechanism to specify what to evaluate in the list. Assuming y has the value 42 in the environment, both (list 'x y 'z) and `(x ,y z) evaluate to (x 42 z).

Armed with this knowledge, the alist in your example should look like the following:

`((:knl-width identity ,(-partial '> 0) "%d")
  (:knl-height identity ,(-partial '> 0) "%d")
  (:knl-margin string-to-number ,(-partial '>= 0) "%.2f")
  (:knl-min-scale string-to-number ,(-partial '> 0) "%.2f")
  (:knl-max-scale string-to-number ,(-partial '> 0) "%.2f"))
wasamasa
  • 21,803
  • 1
  • 65
  • 97