36
votes

Clojure is awesome, we all know this, but that's not the point. I'm wondering what the idiomatic way of creating and managing higher-order functions in a Haskell-like way is. In Clojure I can do the following:

(defn sum [a b] (+ a b))

But (sum 1) doesn't return a function: it causes an error. Of course, you can do something like this:

(defn sum
  ([a] (partial + a)) 
  ([a b] (+ a b)))

In this case:

user=> (sum 1)
#<core$partial$fn__3678 clojure.core$partial$fn__3678@1acaf0ed>
user=> ((sum 1) 2)
3

But it doesn't seem like the right way to proceed. Any ideas?
I'm not talking about implementing the sum function, I'm talking at a higher level of abstraction. Are there any idiomatic patterns to follow? Some macro? Is the best way defining a macro or are there alternative solutions?

3

3 Answers

32
votes

Someone has already implememented this on the Clojure group. You can specify how many args a function has, and it will curry itself for you until it gets that many.

The reason this doesn't happen by default in Clojure is that we prefer variadic functions to auto-curried functions, I suppose.

8
votes

I've played a bit with the functions suggested by amalloy. I don't like the explicit specification of the number of argument to curry on. So I've created my custom macro. This is the old way to specific an high order function:

(defn-decorated old-sum
  [(curry* 3)]
  [a b c]
  (+ a b c))

This is my new macro:

(defmacro defn-ho
  [fn-name & defn-stuff]
  (let [number-of-args (count (first defn-stuff))]
    `(defn-decorated ~fn-name [(curry* ~number-of-args)] ~@defn-stuff)))

And this is the new implicit way:

(defn-ho new-sum [a b c] (+ a b c))

As you can see there is no trace of (curry) and other stuff, just define your currified function as before.

Guys, what do you think? Ideas? Suggestions? Bye!

Alfedo

Edit: I've modified the macro according the amalloy issue about docstring. This is the updated version:

(defmacro defhigh
  "Like the original defn-decorated, but the number of argument to curry on
  is implicit."
  [fn-name & defn-stuff]
  (let [[fst snd] (take 2 defn-stuff)
         num-of-args (if (string? fst) (count snd) (count fst))]
    `(defn-decorated ~fn-name [(curry* ~num-of-args)] ~@defn-stuff)))

I don't like the if statement inside the second binding. Any ideas about making it more succint?

0
votes

This will allow you to do what you want:

(defn curry
  ([f len] (curry f len []))
  ([f len applied]
    (fn [& more]
      (let [args (concat applied (if (= 0 (count more)) [nil] more))]
        (if (< (count args) len)
          (curry f len args)
          (apply f args))))))

Here's how to use it:

(def add (curry + 2)) ; read: curry plus to 2 positions
((add 10) 1) ; => 11

The conditional with the [nil] is meant to ensure that every application ensures some forward progress to the curried state. There's a long explanation behind it but I have found it useful. If you don't like this bit, you could set args as:

[args (concat applied more)]

Unlike JavaScript we have no way of knowing the arity of the passed function and so you must specify the length you expect. This makes a lot of sense in Clojure[Script] where a function may have multiple arities.