3
votes

I'm working on the solution to this koan. I'm stumped as to why my solution does not work, but using the definition of comp does work. When I look at the definition of comp, I see:

(defn comp
  "Takes a set of functions and returns a fn that is the composition
  of those fns.  The returned fn takes a variable number of args,
  applies the rightmost of fns to the args, the next
  fn (right-to-left) to the result, etc."
  {:added "1.0"}
  ([f] f)
  ([f g]
     (fn
       ([] (f (g)))
       ([x] (f (g x)))
       ([x y] (f (g x y)))
       ([x y z] (f (g x y z)))
       ([x y z & args] (f (apply g x y z args)))))
  ([f g h]
     (fn
       ([] (f (g (h))))
       ([x] (f (g (h x))))
       ([x y] (f (g (h x y))))
       ([x y z] (f (g (h x y z))))
       ([x y z & args] (f (g (apply h x y z args))))))
  ([f1 f2 f3 & fs]
    (let [fs (reverse (list* f1 f2 f3 fs))]
      (fn [& args]
        (loop [ret (apply (first fs) args) fs (next fs)]
          (if fs
            (recur ((first fs) ret) (next fs))
            ret))))))

Whereas my solution is very similar:

(defn mycomp
    ([f] f)
    ([f1 f2]
       (fn
        ([] (f1 (f2)))
        ([a] (f1 (f2 a)))
        ([a & more] (f1 (apply f2 a more)))
       )
     )
    ([f1 f2 & fs]
       (let [fxns (reverse (list f1 f2 fs))]
          (fn [& args]
        (loop [ret (apply (first fxns) args) fxns (next fxns)]
           (if fxns
             (recur ((first fxns) ret) (next fxns))
             ret))))))

The big difference between the two, from what I can tell, is that the first definition composes three or more functions, whereas the second definition composes two or more functions.

Please point out what is incorrect about my definition.

1

1 Answers

8
votes

(list f1 f2 fs) looks a likely suspect - the first two are functions, and the last is a list of functions, so you're creating a list containing multiple object types and then treating them uniformly. You could fix that by using list* instead.

But as a broader point: holy jeez, don't try to do "two or more" functions, just do zero or more! No special cases means much less code; clojure.core only has a bunch of unrolled cases for speed. If your skeleton is (fn [& fs] (fn [& args] ...)), your life is much easier.