[alexandria-devel] A fix for `curry'

James M. Lawrence llmjjmll at gmail.com
Tue Nov 1 23:08:59 UTC 2011


Failing test:

==========
diff --git a/tests.lisp b/tests.lisp
index a4a8e55..8aa6730 100644
--- a/tests.lisp
+++ b/tests.lisp
@@ -508,6 +508,16 @@
         (funcall fun 2)))
   4)

+(deftest curry.4
+    (let ((curried (let ((x 1))
+                     (curry (progn
+                              (incf x)
+                              (lambda (y z) (* x y z)))
+                            3))))
+      (list (funcall curried 7)
+            (funcall curried 7)))
+  (42 42))
+
 (deftest rcurry.1
     (let ((r (rcurry '/ 2)))
       (funcall r 8))
==========

Simplest fix:

==========
diff --git a/functions.lisp b/functions.lisp
index 15032be..a2eb1d5 100644
--- a/functions.lisp
+++ b/functions.lisp
@@ -121,10 +121,12 @@ it is called with to FUNCTION."

 (define-compiler-macro curry (function &rest arguments)
   (let ((curries (make-gensym-list (length arguments) "CURRY")))
-    `(let ,(mapcar #'list curries arguments)
-       (declare (optimize (speed 3) (safety 1) (debug 1)))
-       (lambda (&rest more)
-         (apply ,function , at curries more)))))
+    (with-gensyms (fn)
+      `(let ((,fn (ensure-function ,function))
+             ,@(mapcar #'list curries arguments))
+         (declare (optimize (speed 3) (safety 1) (debug 1)))
+         (lambda (&rest more)
+           (apply ,fn , at curries more))))))

 (defun rcurry (function &rest arguments)
   "Returns a function that applies the arguments it is called
==========

Alternate fix:

Since CURRY has some optimizations, we could follow suit by avoiding
an unnecessary binding. I think a function designator which looks like
(function ...) or (quote ...) needs no binding, and that those are the
only binding-free possibilities. I have not proved this, however.

==========
diff --git a/functions.lisp b/functions.lisp
index 15032be..5096456 100644
--- a/functions.lisp
+++ b/functions.lisp
@@ -119,13 +119,21 @@ it is called with to FUNCTION."
       ;; Using M-V-C we don't need to append the arguments.
       (multiple-value-call fn (values-list arguments) (values-list more)))))

-(define-compiler-macro curry (function &rest arguments)
+(defmacro curry-helper (function &rest arguments)
   (let ((curries (make-gensym-list (length arguments) "CURRY")))
     `(let ,(mapcar #'list curries arguments)
        (declare (optimize (speed 3) (safety 1) (debug 1)))
        (lambda (&rest more)
          (apply ,function , at curries more)))))

+(define-compiler-macro curry (function &rest arguments)
+  (if (and (consp function)
+           (member (first function) '(function quote)))
+      `(curry-helper ,function , at arguments)
+      (with-gensyms (fn)
+        `(let ((,fn (ensure-function ,function)))
+           (curry-helper ,fn , at arguments)))))
+
 (defun rcurry (function &rest arguments)
   "Returns a function that applies the arguments it is called
 with and ARGUMENTS to FUNCTION."
==========




More information about the alexandria-devel mailing list