1

Given this example:

(defn foo [a & [b c :as args]](prn args))

Is there any way I can add a fourth, optional argument after b and c?

I've tried this and a few other variations with no success (CompilerException java.lang.RuntimeException: Unexpected parameter):

(defn foo [a & [b c :as args d]](prn d))

..another example (CompilerException java.lang.RuntimeException: Unable to resolve symbol: d in this context)

(defn foo [a & [b c :as args & d]](prn d))

Am I hosed after :as args if I want to add more arguments?

EDIT: This was suggested in IRC, and it works, I just wonder if there's a syntactically simpler way...

(defn foo [a & rst] (let [[b c & d] rst args [b c]] {:a a :b b c :args args :rst rst}))

3 Answers 3

2

If the pair (b, c) has a special relationship, why not put them together in a nested vector []? this will allow to define a specific binding for it (but add a constraint on the input), e.g.

(defn bar [a & [[b c :as args] & rest :as all]]
  {:a a :b b :c c :args args :rest rest :all all})

Then:

> (bar 1)
{:a 1, :b nil, :c nil, :args nil, :rest nil, :all nil}

when you enter the vector [b c] you get the binding with args:

> (bar 1 [2 3])
{:a 1, :b 2, :c 3, :args [2 3], :rest nil, :all ([2 3])}

you can have a vector with one element

> (bar 1 [2] 4)
{:a 1, :b 2, :c nil, :args [2], :rest (4), :all ([2] 4)}

or no element at all

> (bar 1 [] 4 5 6)
{:a 1, :b nil, :c nil, :args [], :rest (4 5 6), :all ([] 4 5 6)}

and with all parameters set

> (bar 1 [2 3] 4 5 6)
{:a 1, :b 2, :c 3, :args [2 3], :rest (4 5 6), :all ([2 3] 4 5 6)}
Sign up to request clarification or add additional context in comments.

Comments

1

You can't control the amount of elements in a destructored rest sequence by destructoring it further.

E. g.

(defn foo [a & [b c :as args]] args)
(foo 1 2 3 4 5)
;; => (2 3 4 5)

Binding the first elements makes no difference. It is the same as if your binding vector was [a & args], only that you have additionally bound the first two elements (b and c) of args by destructoring it further. It is not affected by it.

If you need args to have only the first two variadic args, b and c, and wish to bind the third as an optional d one approach would be:

(defn foo [a & [b c d :as args]]
  (let [args (seq (take 2 args))]
    ;; ...
    ))

Comments

0

(defn foo [a & [b c d :as args]](prn args))

2 Comments

But now d is part of args, that's not what I want. I want to be able to bind b and c to one thing, and d to something else.
Someone suggested this in IRC: (defn foo [a & rst] (let [[b c & d] rst args [b c]] {:a a :b b :c c :args args :rst rst})), which works, but is quite a bit more complicated = )

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.