Михал Марчик ответил на ваш вопрос хорошо, но если вы готовы принять (foo {} rest of the args), то карта с необязательными ключевыми словами в качестве первого аргумента (пустая в этом примере), тогда это более просто код будет работать нормально:
(defn foo [{:keys [a b]} & rest] (list a b rest))
(foo {} 2 3)
=> (nil nil (2 3))
(foo {:a 1} 2 3)
=> (1 nil (2 3))
Работает так же для defmacro
. Преимущество этого заключается в том, что вам не нужно запоминать специальный синтаксис для необязательных параметров ключевых слов и реализовывать код для обработки специального синтаксиса (возможно, другие люди, которые собираются вызывать ваш макрос, более привыкли указывать пары ключ-значение в карта, чем за ее пределами). Недостатком является, конечно, то, что вы всегда должны предоставлять карту, даже если вы не хотите предоставлять аргументы с ключевыми словами.
Небольшое улучшение, когда вы хотите избавиться от этого недостатка, - проверка, предоставили ли вы карту в качестве первого элемента списка аргументов:
(defn foo [& rest]
(letfn [(inner-foo [{:keys [a b]} & rest] (list a b rest))]
(if (map? (first rest))
(apply inner-foo rest)
(apply inner-foo {} rest))))
(foo 1 2 3)
=> (nil nil (1 2 3))
(foo {:a 2 :b 3} 4 5 6)
=> (2 3 (4 5 6))