aboutsummaryrefslogtreecommitdiff
path: root/_tils/2021-04-24-clojure-auto-curry.md
blob: 8a54561fb32a7d3c59b36a18b1d4773ef9d7cea4 (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
---

title: Clojure auto curry

date: 2021-04-24

layout: post

lang: en

ref: clojure-auto-curry

---

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