aboutsummaryrefslogblamecommitdiff
path: root/_tils/2021-04-24-clojure-auto-curry.md
blob: 98d6c9a969ad89fb0590597108a1c1023db78b1f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14













                         
                                                                                                                                                                 


























































                                                                                                                                                                     
---

title: Clojure auto curry

date: 2021-04-24

layout: post

lang: en

ref: clojure-auto-curry

---

Here's a simple macro defined by [Loretta He][lorettahe] to create Clojure functions that are curried on all arguments, relying on Clojure's multi-arity support:

```clojure
(defmacro defcurry
  [fname args & body]
  (let [partials (map (fn [n]
                        `(~(subvec args 0 n) (partial ~fname ~@(take n args))))
                      (range 1 (count args)))]
    `(defn ~fname
       (~args ~@body)
       ~@partials)))
```

A naive `add` definition, alongside its usage and macroexpansion:

```clojure
user=> (defcurry add
         [a b c d e]
         (+ 1 2 3 4 5))
#'user/add

user=> (add 1)
#object[clojure.core$partial$fn__5857 0x2c708440 "clojure.core$partial$fn__5857@2c708440"]

user=> (add 1 2 3 4)
#object[clojure.core$partial$fn__5863 0xf4c0e4e "clojure.core$partial$fn__5863@f4c0e4e"]

user=> ((add 1) 2 3 4 5)
15

user=> (((add 1) 2 3) 4 5)
15

user=> (use 'clojure.pprint)
nil

user=> (pprint
        (macroexpand
         '(defcurry add
            [a b c d e]
            (+ 1 2 3 4 5))))
(def
 add
 (clojure.core/fn
  ([a b c d e] (+ 1 2 3 4 5))
  ([a] (clojure.core/partial add a))
  ([a b] (clojure.core/partial add a b))
  ([a b c] (clojure.core/partial add a b c))
  ([a b c d] (clojure.core/partial add a b c d))))
nil
```

This simplistic `defcurry` definition doesn't support optional parameters, multi-arity, `&` rest arguments, docstrings, etc., but it could certainly evolve to do so.

I like how `defcurry` is so short, and abdicates the responsability of doing the multi-arity logic to Clojure's built-in multi-arity support.
Simple and elegant.

Same Clojure as before, now with auto-currying via macros.

[lorettahe]: http://lorettahe.github.io/clojure/2016/09/22/clojure-auto-curry