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

1

5

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)))
Hic answered 11/8, 2012 at 23:41 Comment(1)
Just found this, and thought it may be perfect for you, and if nothing else, at least helpful...alfredodinapoli.wordpress.com/2011/03/16/…Keyboard
K
5

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.

Keyboard answered 12/8, 2012 at 0:55 Comment(1)
Thank you for this answer as well. It will take me a while to grok and see if it can simplify what I am doing since I'm new to clojure and lisp type programming. Sometimes I know what I want to do but have no idea what it is called and even when I have a good search term, the results for clojure are very limited. Just finding out that what I am trying to do might be called "auto-currying" is a big help.Hic

© 2022 - 2024 — McMap. All rights reserved.