2

I'm trying to create a multiple-arity function in Clojure that, if given a single vector argument, just applies another function to the vector; and if given a vector plus 1 or more functions, maps the composition of those functions over the vector then applies the third function. But somehow I don't seem to be able to access any optional argument outside the first.

Toy example code:

(defn times2 [num] (* 2 num))
(defn square [num] (* num num))
(defn plusfiveall [vec-of-nums] (map #(+ 5 %) vec-of-nums))
(defn toynums
  ([vec-of-nums] 
   (plusfiveall vec-of-nums))
  ([vec-of-nums & [funcs]]
   (let [moremath (comp funcs)]
      (plusfiveall (map moremath vec-of-nums)))))

What I think should happen:

(toynums [1 2 3]) = (6 7 8)

(toynums [1 2 3] times2) = (7 9 11)

(toynums [1 2 3] times2 square) = (7 13 23)

but while the first two of those examples work as expected, the third fails to apply square: instead I get (toynums [1 2 3] times2 square) --> (7 9 11)

In an attempt to drill down on the problem, I've also tried a version without the mapping, and have the same surprising behavior:

(defn comp-nomap 
  ([num] (plusfive num))
  ([num & [funcs]] 
   (let [funmath (comp funcs)] 
     (plusfive (funmath num)))))

(comp-nomap 3) = 8 as expected

(comp-nomap 3 times2) = 11 as expected

but (comp-nomap 3 times2 square) also = 11, rather than 23 like it should be.

help? thanks!

2 Answers 2

2

Your second arity uses destructuring of the varargs which will always extract the first function from the supplied ones. You also need to apply comp function to arguments in a sequence:

(defn comp-nomap
  ([num] (plusfive num))
  ([num & funcs]
    (let [funmath (apply comp funcs)]
      (plusfive (funmath num)))))
Sign up to request clarification or add additional context in comments.

1 Comment

thinking out loud to make sure I get it... my original version destructures the remaining & arguments on the way into the function? so when I tried (comp-nomap 3 times2 square) it saw (times2 square) as the & arguments, pulled out the first of those and assigned it to func, discarding the rest... is that right?
2

This construction produces the expected answers:

(defn toynums
  ([vec-of-nums]
   (plusfiveall vec-of-nums))
  ([vec-of-nums & funcs]
   (let [moremath (apply comp funcs)]
     (plusfiveall (map moremath vec-of-nums)))))

The arguments after the & are automatically collected together into a sequence, so that inside you can use apply, which allows the comp function to have the contents of the sequence passed to it as arguments. So really you had it correct, just missed that one thing. It is 'varadic' arguments you need to look up.

1 Comment

thank you! the same strategy works with my even smaller toy example too. Can you/anyone explain why? I'm starting to suspect that the answer involves the mysteries of destructuring, and I don't understand it at all...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.