Search code examples
macroscommon-lispelisp

Macro treat keys as true when defined


Is is possible to treat a key as true when using cl-defmacros in elisp? For example,

(cl-defmacro mac (&key a b c)
  `(,@(if a "a" (if b "b" "c"))))

(mac :a)
"c"

Can I have that evaluate to "a" without needing to give :a a value like (mac :a t). I don't know if the same semantics apply to common-lisp


Solution

  • There is no way to do this with the base language. One could define a defun like macro to do this. Would you want to only allow these to be set to true or would you want to give values other than t (or nil be omission)? The first case can be done by writing some macros. The second case can’t be done in a nice way (specifically would (mac :a :b) do a <- t; b <- t or a <- :b?)

    Here is something you could write for Common Lisp:

    (defmacro keyset-bind (keys-and-vars form &body body)
      (let (syms vars
            (keyv (gensym "KEY")))
        (loop for key in keys-and-vars
          for sym = (if (consp key)
                        (car key)
                        (intern (symbol-name key) "KEYWORD"))
          for var = (if (consp key) (cadr key) key)
          collect sym into symst
          collect var into varst
          finally (setf syms symst vars varst))
        `(let ,vars
            (loop for ,keyv in ,form
              do (case ,keyv
                   ,@(loop for sym in syms
                       for var in vars
                       collect `((,sym) (setf ,var t)))
                   (t (error "unrecognised keyword ~a" ,keyv))))
            ,@body)))
    

    And then you can use it like:

    (defun mac (&rest switches)
      (keyset-bind (a b (:t foo)) switches
        (list a b foo)))
    
    CL-USER> (mac :a)
    (T NIL NIL)
    CL-USER> (mac :b :t)
    (NIL NIL T)
    CL-USER> (mac :baz)
    ;; Error ...
    

    One could write an alternative defun which accepted eg a &switch argument type in this way but then you would need to decide how to make it interact with other argument types and keywords and &allow-other-keys and :allow-other-keys t. In general all of this is a bad idea and any kind of mixing with (or even usage of) the above is discouraged as it makes it hard to know whether an unfamiliar function call will be interpreted as normal keywords or set-of-keywords. If you want a language which supports this kind of thing, look at perl6 where keywords are a key-value-pair type and there is special syntax for key => true and key => false.