David Weiser
David Weiser

Reputation: 5205

Function composition failure

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.

Upvotes: 3

Views: 200

Answers (1)

amalloy
amalloy

Reputation: 92117

(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.

Upvotes: 8

Related Questions