Search code examples
clojurepartiallambdacombinatorsarity

Can clojure evaluate a chain of mixed arity functions and return a partial function if needed?


Suppose you have three functions of arity 1, 2 and 3 as below:

(defn I [x] x)
(defn K [x y] x)
(defn S [x y z] (x z (y z)))

Does clojure have an evaluation function or idiom for evaluating:

(I K S I I) as (I (K (S (I (I)))))

returning a parital function of arity 2?

I am considering creating a macro that can take the simple function definitions above and expand them to multi-arity functions that can return partial results. I would not want to create the macro if there is already a built in or idiomatic way to accomplish this.

Here is what the expanded macros would like for the above functions:

(defn I
  ([x] I x)
  ([x & more] (apply (I x) more)))

(defn K
  ([x] (partial K x))
  ([x y] x)
  ([x y & more] (apply (K x y) more)))

(defn S
  ([x] (partial S x))
  ([x y] (partial S x y))
  ([x y z] (x z (y z)))
  ([x y z & more] (apply (S x y z) more)))

Solution

  • I'm not sure I fully understand what you are trying to do, but the comp function is useful for doing this kind of "function chaining" you seem to be talking about. For example:

    user> ((comp vec rest list) 1 2 3 4 5)
    => [2 3 4 5]
    

    Which is equivalent to:

    user> (vec (rest (list 1 2 3 4 5)))
    => [2 3 4 5]
    

    In your case, if you have the list (I K S I I), and you want to evaluate it as (I (K (S (I (I))))), I would use (reduce comp ...), but you could also use (apply comp ...).

    user> ((reduce comp [vec rest list]) 1 2 3 4 5)
    => [2 3 4 5]
    user> ((apply comp [vec rest list]) 1 2 3 4 5)
    => [2 3 4 5]
    

    You may also be interested in the -> or ->> macros. These macros nest their arguments sequentially into the next arguments. The -> macro will nest into the first position of the next expression, whereas the ->> macro will nest into the last position of the next expression. If the "next thing" is a function, both will behave the same, and form an expression of (function nested-things-so-far), and continue along.

    Really, examples are best:

    (-> 1 (+ 10) (- 100) inc)
    ;//Expands to...
    (inc (- (+ 1 10) 100))
    ;//Evaluating in the REPL...
    user> (-> 1 (+ 10) (- 100) inc)
    => -88
    
    (->> 1 (+ 10) (- 100) inc)
    ;//Expands to...
    (inc (- 100 (+ 10 1)))
    ;//Evaluating in the REPL...
    user> (-> 1 (+ 10) (- 100) inc)
    => 90
    

    However, it seems more like you want to do something involving auto-currying (although, again, I don't think I fully understand), and for that I don't know of anything pre-existing built-in way.