From 960e4410f76801356ebd42801c914b2910a302a7 Mon Sep 17 00:00:00 2001
From: EuAndreh <eu@euandre.org>
Date: Mon, 18 Nov 2024 08:21:58 -0300
Subject: v0 migration to mkwb

---
 src/content/tils/2021/04/24/clojure-autocurry.adoc | 135 +++++++++++++++++++++
 1 file changed, 135 insertions(+)
 create mode 100644 src/content/tils/2021/04/24/clojure-autocurry.adoc

(limited to 'src/content/tils/2021/04/24/clojure-autocurry.adoc')

diff --git a/src/content/tils/2021/04/24/clojure-autocurry.adoc b/src/content/tils/2021/04/24/clojure-autocurry.adoc
new file mode 100644
index 0000000..c1e277f
--- /dev/null
+++ b/src/content/tils/2021/04/24/clojure-autocurry.adoc
@@ -0,0 +1,135 @@
+---
+
+title: Clojure auto curry
+
+date: 2021-04-24 1
+
+updated_at: 2021-04-27
+
+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
+  [name args & body]
+  (let [partials (map (fn [n]
+                        `(~(subvec args 0 n) (partial ~name ~@(take n args))))
+                      (range 1 (count args)))]
+    `(defn ~name
+       (~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
+
+## Comparison with Common Lisp
+
+My attempt at writing an equivalent for Common Lisp gives me:
+
+```lisp
+(defun partial (fn &rest args)
+  (lambda (&rest args2)
+    (apply fn (append args args2))))
+
+(defun curry-n (n func)
+  (cond ((< n 0) (error "Too many arguments"))
+        ((zerop n) (funcall func))
+        (t (lambda (&rest rest)
+             (curry-n (- n (length rest))
+                      (apply #'partial func rest))))))
+
+(defmacro defcurry (name args &body body)
+  `(defun ,name (&rest rest)
+     (let ((func (lambda ,args ,@body)))
+       (curry-n (- ,(length args) (length rest))
+                (apply #'partial func rest)))))
+```
+
+Without built-in multi-arity support, we have to do more work, like tracking the number of arguments consumed so far.
+We also have to write `#'partial` ourselves.
+That is, without dependending on any library, sticking to ANSI Common Lisp.
+
+The usage is pretty similar:
+
+```lisp
+* (defcurry add (a b c d e)
+    (+ a b c d e))
+ADD
+
+* (add 1)
+#<FUNCTION (LAMBDA (&REST REST) :IN CURRY-N) {100216419B}>
+
+* (funcall (add 1) 2 3 4)
+#<FUNCTION (LAMBDA (&REST REST) :IN CURRY-N) {100216537B}>
+
+* (funcall (add 1) 2 3 4 5)
+15
+
+* (funcall (funcall (add 1) 2 3) 4 5)
+15
+
+* (macroexpand-1
+    '(defcurry add (a b c d e)
+       (+ a b c d e)))
+(DEFUN ADD (&REST REST)
+  (LET ((FUNC (LAMBDA (A B C D E) (+ A B C D E))))
+    (CURRY-N (- 5 (LENGTH REST)) (APPLY #'PARTIAL FUNC REST))))
+T
+```
+
+This also require `funcall`s, since we return a `lambda` that doesn't live in the function namespace.
+
+Like the Clojure one, it doesn't support optional parameters, `&rest` rest arguments, docstrings, etc., but it also could evolve to do so.
-- 
cgit v1.2.3

