Search code examples
clojureidioms

How to pass the rest args of a variadic function to another function?


I want to write a function which simply updates a vector in a map with new value, but can take any number of args, but at least one.

Here is example:

(defn my-update [what item & items]
  (update what :desired-key conj item items))

Unfortunately, this doesn't work. Despite that update do have a signature with multiple values (like [m k f x y]), all remaining arguments to my-update will be joined into one sequence, which will be passed to conj as one argument.

Instead, wrapping conj with apply in an anonymous function does work, but looks not so elegant:

(defn my-update [what item & items]
  (update what :desired-key #(apply conj % item items))

What is the idiomatic way of writing such a function like my-update?


Solution

  • You can simply insert apply before update. That will call the function update with the arguments that follows except for the last argument which should be a sequence, whose elements become the remaining arguments in the call:

    (defn my-update [what item & items]
      (apply update what :desired-key conj item items))
    
    (my-update {:desired-key [0]} 1 2 3 4)
    ;; => {:desired-key [0 1 2 3 4]}
    
    (my-update {:desired-key [0]})
    ;; Exception: Wrong number of args (1) passed to: my-update
    

    This way, you can keep the function argument list [what item & items] that makes it clear that at least one item needs to be provided.

    In general, a call (apply f a b c ... [x y z ...]) will evaluate to the same as (f a b c ... x y z ...).