From: szymon
Subject: defun-curry finished. (was: Useful (I hope) trick.)
Date: 
Message-ID: <87658uumyh.fsf@eva.rplacd.net>
Hi. I rewrote my macro - now it can handle lambda keyword args. Plese
test/critique it.

Some examples:

(defun defun-curry-examples (&key reset)
  (if (eq reset t) (mapc #'fmakunbound
			 '(simple-example-1 simple-example-2 rest-example key-example_make-list)))
  (unless (fboundp 'simple-example-1)
    (defun-curry simple-example-1 (a b c d e) (list a b c d e)))
  (unless (fboundp 'simple-example-2)
    (defun-curry simple-example-2 (a b c) (concatenate 'string a b c)))
  (unless (fboundp 'rest-example)
    (defun-curry rest-example (a &rest r) (list a r)))
  (unless (fboundp 'key-example_make-list)
    (defun-curry key-example_make-list (n &key initial-element) (make-list n :initial-element initial-element)))
  (flet
      ((xxx ()
	    (values (mapcar (foo-1 1 2 3 4 ~) '(D E F G))
		    (mapcar (foo-1 1 ~ 2 ~ 3) '(a c) '(b d))
		    (mapcar (foo-2 ~ "-" ~) '("foo" "1" "A") '("bar" "2" "B"))
		    (funcall (foo-2 ~ ~ ~) "a" "b" "c")
		    (foo-2 "A" "B" "C")
		    (funcall (rest-example ~ 1 2 3 ~ 5 ~ ~) 'I 'IV 'VI 'VII)
		    (mapcar (key-example_make-list 3 :initial-element ~) '(foo bar baz)))))
    (multiple-value-bind (a b c d e f g) (xxx)
      (values (function-lambda-expression #'xxx)
	      '----------------------------------------------------------
	      a b c d e
	      '----------------------------------------------------------
	      f g))))


CL-USER> (defun-curry-examples)

==> (LAMBDA ()
	    (BLOCK XXX
		   (VALUES (MAPCAR (FOO-1 1 2 3 4 ~) '(D E F G))
			   (MAPCAR (FOO-1 1 ~ 2 ~ 3) '(A C) '(B D))
			   (MAPCAR (FOO-2 ~ "-" ~) '("foo" "1" "A") '("bar" "2" "B"))
			   (FUNCALL (FOO-2 ~ ~ ~) "a" "b" "c")
			   (FOO-2 "A" "B" "C")
			   (FUNCALL (REST-EXAMPLE ~ 1 2 3 ~ 5 ~ ~) 'I 'IV 'VI 'VII)
			   (MAPCAR (KEY-EXAMPLE_MAKE-LIST 3 :INITIAL-ELEMENT ~)
				   '(FOO BAR BAZ)))))
----------------------------------------------------------
((1 2 3 4 D) (1 2 3 4 E) (1 2 3 4 F) (1 2 3 4 G))
((1 A 2 B 3) (1 C 2 D 3))
("foo-bar" "1-2" "A-B")
"abc"
"ABC"
----------------------------------------------------------
(I (1 2 3 IV 5 VI VII))
((FOO FOO FOO) (BAR BAR BAR) (BAZ BAZ BAZ))


Code:

;;; macro: defun-curry

;; This macro is not well tested. Plese be cautious.
;; Any comments or patches greatly appreciated.
;; Combination: `&rest' with `&key' (and `&allow-other-keys') is not
;; implemented in effective way, sorry.
;; This is allowed [1] (but is not as fast as might be):
;;
;; (defun-curry foo (a b &rest r &key kw-1 kw-2 &allow-other-keys)
;;	   (values (list 'REQUIRED (list a b))
;;		   (list 'KEYWORD (list kw-1 kw-2))
;;		   (list 'REST r)))
;;
;; (funcall (foo 1 2 3 4 :kw-1 ~ :kw-2 ~ 5 6) 'i 'ii)
;;
;; ==> (REQUIRED (1 2))
;;     (KEYWORD (I II))
;;     (REST (3 4 :KW-1 II :KW-2 I 5 6))
;; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
;; [1] but counting (partially) is performed 'twice' - I should rewrite this.
;; I belive that all other lambda key'combinations' are written properly
;; (it terms of speed).

(defmacro defun-curry (name (&rest args) &body body)

  (unless (fboundp 'memq) (defun memq (item lst) (member item lst :test #'eq)))

  (labels ((iota (n &aux (c -1))
		 (mapcar (lambda (x) (declare (ignore x)) (incf c)) (make-list n)))

	   (nsub-into (list pred &key (key #'identity))
		      (prog1 list
			(mapc (lambda (x) (setq list (nsubstitute (funcall key x) x list)))
			      (remove-if-not pred list))))

	   (sub-into (list pred &key (key #'identity))
		     (let ((list (copy-tree list)))
		       (nsub-into list pred :key key)))

	   (flatten-lambda-list (list) (sub-into list #'listp :key #'car)))

    (let* ((lambda-keyword-list (list '&required '&optional '&key '&rest '&aux))
	   (lambda-vect (apply #'vector (mapcar #'list lambda-keyword-list)))
	   (args (copy-tree args))
	   (args-oryginal (copy-tree args))
	   (fn-1 (gensym (symbol-name name)))
	   (spec (let ((g (gensym "S"))) (set g g)))
	   (2g-symb-list		; 'double gensymmed' symbols
	    '(*lambda-list* *lambda-global*))
	   (1g-symb-list		; 'single gensymmed' symbols
	    '(key-sublist tmp-list plug kw-symbol-list *vector-1* *vector-2* *vector-1-fill* *vector-2-fill* *fill-grafts* temp-function skeleton *global-vector* *global-vector-length* *global-hash* allow-o-keys copy-rest dummy-tail dummy-index g-index memq memq-rest revised-req-ll)))
      (progv
	  (append 1g-symb-list 2g-symb-list lambda-keyword-list)
	  (append (mapcar (lambda (symb) (gensym (symbol-name symb)))
			  (append 1g-symb-list 2g-symb-list))
		  (iota (length lambda-keyword-list)))
	(mapc (lambda (symb) (set (symbol-value symb) (gensym))) 2g-symb-list)

	(setq allow-o-keys (if (memq '&allow-other-keys args) t)
	      args (delete '&allow-other-keys args))

	(let ((index-symbol (car lambda-keyword-list)) tmp)
	  (mapc (lambda (i)
		  (if (setq tmp (memq i lambda-keyword-list))
		      (setq index-symbol (car tmp))
		    (rplacd (last (svref lambda-vect (symbol-value index-symbol))) (list i))))
		args))
	(map nil (lambda (x) (set (car x) (cdr x))) lambda-vect)
	
	(setq revised-req-ll
	      (append &required (flatten-lambda-list &optional) (flatten-lambda-list &key)))

	`(progn

	   (unless (and (boundp '~) (symbolp ~) (eq ~ (symbol-value ~))) (defconstant ~ ,spec))

	   (defparameter ,*global-hash*
	     (make-hash-table :size 65 :test #'eq))

	   (defparameter ,*global-vector*
	     (make-array ,(if &rest 50
			    (if (> (length revised-req-ll) 49)
				50
			      (length revised-req-ll)))
			 :element-type 'list
			 :initial-element nil))

	   (defparameter ,*global-vector-length* (length ,*global-vector*))

	   (defparameter ,fn-1
	     (lambda ,(append revised-req-ll (if &rest `(&rest ,@&rest)) (if &aux `(&aux ,@&aux)))
	       ,@body))

	   (setq ,*vector-1*
		 (make-array ,(length revised-req-ll) :element-type 'list :initial-element nil))

	   (flet ((,skeleton
		   (no-of-args)
		   (let ((skeleton-lambda-list
			  (loop repeat no-of-args collect (gensym))))
		     `(lambda ,skeleton-lambda-list
			(setq ,,*lambda-global* (list ,@skeleton-lambda-list))
			(funcall ,,*fill-grafts*)
			(apply ,,fn-1 ,,*lambda-list*)))))

	     (let ,(append `(,temp-function ,memq)
			   (if &rest
			       (list memq-rest g-index copy-rest))
			   (and &rest &key allow-o-keys `(,tmp-list (,plug (gensym)))))

	       (defparameter ,*fill-grafts*
		 (lambda ()
		   ,@(append
		      `((dotimes (,dummy-index ,*vector-1-fill*)
			  (rplaca (svref ,*vector-1* ,dummy-index)
				  (pop ,(symbol-value *lambda-global*)))))

		      (if &rest `((dotimes (,dummy-index ,*vector-2-fill*)
				    (rplaca (svref ,*vector-2* ,dummy-index)
					    (pop ,(symbol-value *lambda-global*))))))

		      (and &rest &key allow-o-keys
			   `((mapc (lambda (x)
				     (rplaca x (nth (cdr (pop ,kw-symbol-list))
						    ,(symbol-value *lambda-list*))))
				   ,tmp-list))))))

	       (defun ,name ,args-oryginal
		 
		 ,(append `(setq ,(symbol-value *lambda-list*) (list ,@revised-req-ll)
				 ,memq (memq ~ ,(symbol-value *lambda-list*)))
			  (if &rest
			      `(,*vector-2* (make-array (length ,@&rest)
							:element-type 'list :initial-element nil)
					    ,copy-rest (copy-tree ,@&rest)
					    ,memq-rest (memq ~ ,copy-rest))))

		 ,@(and &rest &key allow-o-keys
			`((setq ,tmp-list nil
				,kw-symbol-list (delete-if
						 #'not
						 (list ,@(loop for * in (flatten-lambda-list &key) collect
							       `(cons ,(intern (symbol-name *) 'keyword)
								      (if (eq ,* ~)
									  (position ',*
										    (quote ,revised-req-ll)
										    :test #'eq)))))
						 :key #'cdr))
			  (mapl (lambda (,dummy-tail)
				  (and (member (car ,dummy-tail) ,kw-symbol-list :test #'eq :key #'car)
				       (eq (cadr ,dummy-tail) ~)
				       (push (cdr ,dummy-tail) ,tmp-list)))
				,copy-rest)
			  (mapc (lambda (x) (rplaca x ,plug)) ,tmp-list)))

		 (if ,(if &rest `(or ,memq ,memq-rest) memq)
		     (progn
		       ,@(append
			  `((setq ,*vector-1-fill* 0)
			    (mapl (lambda (,dummy-tail)
				    (when (eq (car ,dummy-tail) ~)
				      (setf (svref ,*vector-1* ,*vector-1-fill*)
					    ,dummy-tail)
				      (incf ,*vector-1-fill*)))
				  ,memq))

			  (if &rest
			      `((setq ,*vector-2-fill* 0)
				(mapl (lambda (,dummy-tail)
					(when (eq (car ,dummy-tail) ~)
					  (setf (svref ,*vector-2* ,*vector-2-fill*)
						,dummy-tail)
					  (incf ,*vector-2-fill*)))
				      ,memq-rest)))

			  `((setq ,g-index
				  ,(if &rest
				       `(+ ,*vector-1-fill* ,*vector-2-fill*)
				     *vector-1-fill*)
				  ,@(if &rest
					`(,(symbol-value *lambda-list*)
					  (nconc ,(symbol-value *lambda-list*)
						 ,copy-rest))))))

		       (cond ((< ,g-index ,*global-vector-length*)
			      (setq ,temp-function (svref ,*global-vector* ,g-index))
			      (if ,temp-function
				  ,temp-function
				(setf (svref ,*global-vector* ,g-index)
				      (compile nil (,skeleton ,g-index)))))
			     (t
			      (setq ,temp-function (gethash ,g-index ,*global-hash*))
			      (if ,temp-function
				  ,temp-function
				(setf (gethash ,g-index ,*global-hash*)
				      (compile nil (,skeleton ,g-index)))))))

		   ,@body)))))))))

;; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

From: szymon
Subject: Re: defun-curry finished. (was: Useful (I hope) trick.)
Date: 
Message-ID: <87smbvlxmd.fsf@eva.rplacd.net>
szymon <···@bar.baz> writes:

> Hi. I rewrote my macro - now it can handle lambda keyword args. Plese
> test/critique it.
> 
> Some examples:

> (defun defun-curry-examples (&key reset)
> [...]
> 	      f g))))


Example function won't work, sorry. I rewrite it.

new example function output:

----------------------------------------------------------------------
(DEFUN-CURRY SIMPLE-EXAMPLE-1 (A B C D E) (LIST A B C D E))

(MAPCAR (SIMPLE-EXAMPLE-1 1 2 3 4 ~) '(D E F G))

===> ((1 2 3 4 D) (1 2 3 4 E) (1 2 3 4 F) (1 2 3 4 G))
----------------------------------------------------------------------
(DEFUN-CURRY SIMPLE-EXAMPLE-2 (A B C) (CONCATENATE 'STRING A B C))

(MAPCAR (SIMPLE-EXAMPLE-2 ~ - ~) '(foo 1 A) '(bar 2 B))

===> (foo-bar 1-2 A-B)
----------------------------------------------------------------------
(DEFUN-CURRY REST-EXAMPLE (A &REST R) (LIST A R))

(FUNCALL (REST-EXAMPLE ~ 1 2 3 ~ 5 ~ ~) 'I 'IV 'VI 'VII)

===> (I (1 2 3 IV 5 VI VII))
----------------------------------------------------------------------
(DEFUN-CURRY KEY-EXAMPLE_MAKE-LIST
             (N &KEY INITIAL-ELEMENT)
             (MAKE-LIST N INITIAL-ELEMENT INITIAL-ELEMENT))

(MAPCAR (KEY-EXAMPLE_MAKE-LIST 3 INITIAL-ELEMENT ~) '(FOO BAR BAZ))

===> ((FOO FOO FOO) (BAR BAR BAR) (BAZ BAZ BAZ))
----------------------------------------------------------------------



example function:

(defun defun-curry-examples (&key reset)
  (setq list-of-efuns
	(list 'simple-example-1 'simple-example-2 'rest-example 'key-example_make-list))

  (when (eq reset t) (mapc #'fmakunbound list-of-efnuns))

  ;; [1]
  (defun-curry simple-example-1 (a b c d e)
    (list a b c d e))
  (defvar simple-example-1
    '(mapcar (simple-example-1 1 2 3 4 ~) '(D E F G)))
  (setf (get 'simple-example-1 :definition)
	'(defun-curry simple-example-1 (a b c d e) (list a b c d e)))

  ;; [2]
  (defun-curry simple-example-2 (a b c)
    (concatenate 'string a b c))
  (defvar simple-example-2
    '(mapcar (simple-example-2 ~ "-" ~) '("foo" "1" "A") '("bar" "2" "B")))
  (setf (get 'simple-example-2 :definition)
	'(defun-curry simple-example-2 (a b c) (concatenate 'string a b c)))

  ;; [3]
  (defun-curry rest-example (a &rest r)
    (list a r))
  (defvar rest-example
    '(funcall (rest-example ~ 1 2 3 ~ 5 ~ ~) 'I 'IV 'VI 'VII))
  (setf (get 'rest-example :definition)
	'(defun-curry rest-example (a &rest r) (list a r)))

  ;; [4]
  (defun-curry key-example_make-list (n &key initial-element)
    (make-list n :initial-element initial-element))
  (defvar key-example_make-list
    '(mapcar (key-example_make-list 3 :initial-element ~) '(foo bar baz)))
  (setf (get 'key-example_make-list :definition)
	'(defun-curry key-example_make-list (n &key initial-element)
	   (make-list n :initial-element initial-element)))

  (let* ((l '(lambda () *))
	 (output
	  (mapcan (lambda (symb)
		    (list (get symb :definition)
		          (symbol-value symb)
			  (funcall (compile nil
					    (substitute (copy-tree
							 (symbol-value symb))
							'*
							(copy-tree l))))))
		  list-of-efuns)))
    (let ((separator (concatenate 'string "~%" (make-string 70  :initial-element #\-) "~%")))
      (format t (concatenate 'string
			     separator "~{~A~2%~A~2%===> ~A" separator "~}")
	      output))))

Regards, Szymon.
From: Joerg Hoehle
Subject: Re: defun-curry finished. (was: Useful (I hope) trick.)
Date: 
Message-ID: <u3c3s88p4.fsf@users.sourceforge.net>
szymon <···@bar.baz> writes:

> Hi. I rewrote my macro - now it can handle lambda keyword args. Plese
> test/critique it.

1. Your rewrite obviously did not consider my response to your initial post in
Message-Id: <·············@users.sourceforge.net>
Please read it.

2. Furthermore,
>(defun ...
>  (unless (fboundp 'memq) (defun memq (item lst) ...
Don't do that. Either you defun at top-level, or you don't.
Same for the ~ defconstant (see my other message).

3. A test case of min
[89]> (defun-curry foo-1 (a b c d) (list a b c d))
FOO-1
[90]> (setq f1 (foo-1 ~ ~ 3 4))
WARNING :
#:G687 is neither declared nor bound,
it will be treated as if it were declared SPECIAL.
WARNING :
#:G686 is neither declared nor bound,
it will be treated as if it were declared SPECIAL.
;; Very probably an indication that something is wrong.
[92]> (setq f2 (foo-1 ~ 2 ~ 4))
[93]> (funcall f1 'a 'b)
(A 2 B 4) -- Oops!
This fails in both old and new versions.

I hardly looked at the new version (too long), sorry

5. I think you've too many globals and specials in a wrong quest for
speed. Go for closures over lexical variables first, they will give
you correctness (and they're fast too, faster than globals).

You'll have to think carefully about each variable: at what time do I
need it? when foo-1 is executed or when the macro is defined? Does it
need to be closed over, i.e. preseved from definition time to
invocation time?

6. about using COMPILE in the code:
>			      (compile nil (,skeleton ,g-index)))))
I didn't check why this would be needed. I tend to think it's a
misguided attempt to avoid EVAL of which you can read everywhere that
it's evil. Since EVAL is not needed to write your macro (believe me),
I fail to see why COMPILE would be needed.


I'd recommend you restart from your old code (witout &optional
etc. overhead). You can then write the macro in a clear, concise and
correct way (in half a screenful).

I recommend you start by writing the whole code without a single SETQ,
and without seeking any caching.

Regards,
	Jorg Hohle
Telekom/T-Systems Technology Center
From: Szymon
Subject: Re: defun-curry finished. (was: Useful (I hope) trick.)
Date: 
Message-ID: <87r7racqr7.fsf@eva.rplacd.net>
Hi.

I don't really know how it happened that I overlooked your first post.
Sorry.

---------------------

Thanks for the tips - I intend to follow them.  This present post of mine
is rather brief as I am busy at the moment and am afraid that for the next
few days the situation will remain the same.

I know where the error is (in fact, an major omission) that you pointed
out.  Its removal should take c. 20 minutes of work (sounds optimistic)...

And later I'll rewrite the whole (I'll also try to give a proper answer
to your posts).

---------------------

As far as COMPILE (or EVAL/COERCE) is concerned, in my opinion it is
necessary (at least I don't have any idea how to solve it in a different
way).  The snag is that the macro is to serve the &REST lambda keyword, and
in this connection the maximum number of '~' is not determined in advance.
I know that I could use the &REST in lambda-function which is returned, but
then a user might give more arguments than the earlier '~' - which I want
to _avoid_.  In this case, COMPILE is indispensable, in my opinion.  I did
not hesitate to use this operator (i.e. COMPILE), the time 'excess' should
be small as compiled are only the necessary elements (besides I memoize
things that I have compiled [1]) - the whole work is performed by the
function '*fill-grafts*'.  The globals, which you mentioned, result, among
others, from the fact that COMPILE does not take into consideration the
lexical environment of the compiled content.  In this connection, the
information between the dynamically compiled function and the
'*fill-grafts*' is 'transmitted' by globals.  To sum up, perhaps I defined
too many globals, but a part of them is indispensable.  The code may give
an impression that there are more globals than in reality; those which can
be [2] were 'declared' by me with LET, yet later I use (or abuse) SETQ
which may suggest that the globals are used when they are not.

---------------------

As for SFRI - it is really similar. As can be seen, it is difficult to
discover something original :/ Still, I am happy to have hit upon a good
idea :)

---------------------

Btw, it seems to me that it was useful to implement a part of SFRIs in CL
(or maybe somebody has already done it?).  Obviously I mean this part which
is not a faked elements of the CL standard library.


Regards, Szymon.


[1] The first fifty items (functions compiled on request) '~' are memoized
into the vector, the rest is stored in the hash table.

[2] At least it seemed so to me. I have just checked - definitely there are
too many globals.
From: Szymon
Subject: Re: defun-curry finished. (was: Useful (I hope) trick.)
Date: 
Message-ID: <87pt6pm5dd.fsf@eva.rplacd.net>
Joerg Hoehle <······@users.sourceforge.net> writes:

> szymon <···@bar.baz> writes:
> 
> > Hi. I rewrote my macro - now it can handle lambda keyword args. Plese
> > test/critique it.
> 
> [.....]
> 
> 3. A test case of min
> [89]> (defun-curry foo-1 (a b c d) (list a b c d))
> FOO-1
> [90]> (setq f1 (foo-1 ~ ~ 3 4))
> WARNING :
> #:G687 is neither declared nor bound,
> it will be treated as if it were declared SPECIAL.
> WARNING :
> #:G686 is neither declared nor bound,
> it will be treated as if it were declared SPECIAL.
> ;; Very probably an indication that something is wrong.
> [92]> (setq f2 (foo-1 ~ 2 ~ 4))
> [93]> (funcall f1 'a 'b)
> (A 2 B 4) -- Oops!
> This fails in both old and new versions.

with new code:

CL-USER> (defun-curry foo-1 (a b c d) (list a b c d))
; Converted FOO-1.
FOO-1
CL-USER> (setq f1 (foo-1 ~ ~ 3 4))
; Compiling LAMBDA (~::A2P ~::A2Q): 
; Compiling Top-Level Form: 
#<Function "LAMBDA (~::A2P ~::A2Q)" {587BE9F1}>
CL-USER> (setq f2 (foo-1 ~ 2 ~ 4))
; Compiling LAMBDA (~::A2P ~::A2Q): 
; Compiling Top-Level Form: 
#<Function "LAMBDA (~::A2P ~::A2Q)" {587D97E1}>
CL-USER> (funcall f1 'a 'b)
(A B 3 4)
CL-USER> 

CODE:

;;; defun-curry -- very crude solution :(
;;;
;;; Simplified (no &REST, &KEY, &STUFF).
;;; Bug fixed - thanks to Joerg Hoehle for the report.
;;;
;;; (progn (make-defun-curry) (defun-curry foo (a b c d) (list a b c d)) (setq f1 (foo ~ ~ 3 4) f2 (foo 1 ~ 3 ~)) (values (funcall f1 'a 'b) (funcall f2 'a 'b)))
;;;
;;; ===> (A B 3 4)
;;; ===> (1 A 3 B)

(defmacro make-defun-curry (&key (initial-symb-list-length 99)
				 (spec-symb-holder '~)
				 (pac-string "~"))
  (let ((d-lst (gensym)) (vector-of-shared-symbol-lists (gensym))  (I (gensym)))
    `(progn
       (defconstant ,spec-symb-holder (gensym "~S~"))
       (defconstant ,vector-of-shared-symbol-lists
	 (make-array (1+ ,initial-symb-list-length) :element-type 'LIST :initial-element NIL))
       (defpackage ,pac-string (:use ,pac-string) (:size ,initial-symb-list-length))
       (loop for ,d-lst on (loop for ,I from 0 below ,initial-symb-list-length
				 collect (let ((*print-base* 36))
					   (intern (format nil "A~A" ,I) ,pac-string)))
	     for ,I = ,initial-symb-list-length then (1- ,I)
	     do (setf (svref ,vector-of-shared-symbol-lists ,I) ,d-lst))
       (defmacro defun-curry (name (&rest args) &body body)
	 (let* ((1-GS '(*hash* *fn* *do-job* skeleton temp-var-1 temp-var-2 ~positions))
		(2-GS '(*lambda* *cut-lambda*))
		(AS (append 1-GS (copy-list 2-GS))))
	   (progv AS (loop repeat (length AS) collect (gensym))
	     (mapc (lambda (symb) (set (symbol-value symb) (gensym))) 2-GS)
	     `(progn
		(defvar ,*lambda*)
		(defvar ,*cut-lambda*)
		(defvar ,(symbol-value *lambda*))
		(defvar ,(symbol-value *cut-lambda*))
		(defvar ,*hash* (make-hash-table :size 65 :test #'equal))
		(defvar ,*do-job* (lambda (&aux (tail ,(symbol-value *lambda*)))
				    (loop (if (eq ',,spec-symb-holder (car tail))
					      (rplaca tail (pop ,(symbol-value *cut-lambda*))))
					  (unless (setq tail (cdr tail)) (return)))))
		(defvar ,*fn* (lambda ,args ,@body))
		(flet ((,skeleton
			(no-of-args
			 &aux (skeleton-lambda-list (if (> no-of-args ,,initial-symb-list-length)
							(loop repeat no-of-args collect (gensym "A"))
						      (svref ,,vector-of-shared-symbol-lists no-of-args))))
			`(lambda ,skeleton-lambda-list
			   (setq ,,*cut-lambda* (list ,@skeleton-lambda-list)
				 ,,*lambda* (list
					     ,@(mapcar (lambda (,temp-var-2) `',,temp-var-2)
						       (symbol-value ,*lambda*))))
			   (funcall ,,*do-job*)
			   (apply ,,*fn* ,,*lambda*))))
		  (let (,temp-var-1)
		    (defun ,name ,args
		      (setq ,(symbol-value *lambda*) (list ,@args))
		      (unless (memq ',,spec-symb-holder ,(symbol-value *lambda*))
			(return-from ,name ,@body))
		      (if (setq ,temp-var-1 (gethash ,(symbol-value *lambda*) ,*hash*))
			  (return-from ,name ,temp-var-1)
			(setf (gethash ,(symbol-value *lambda*) ,*hash*)
			      (compile nil (,skeleton (count ',,spec-symb-holder ,(symbol-value *lambda*))))))))))))))))

;;; end.

Regards, Szymon.
From: Szymon
Subject: Re: simple defun-curry without COMPILE (was: defun-curry finished. (was: Useful (I hope) trick.))
Date: 
Message-ID: <873c3erjn9.fsf_-_@eva.rplacd.net>
Joerg Hoehle <······@users.sourceforge.net> writes:

> I'd recommend you restart from your old code (witout &optional
> etc. overhead). You can then write the macro in a clear, concise and
> correct way (in half a screenful).
> 
> I recommend you start by writing the whole code without a single SETQ,
> and without seeking any caching.

Ok. Thanks.


;;; - CODE -
(defmacro make-defun-curry (&key (spec-symb '~))
  `(progn (defconstant ,spec-symb (gensym "~S~"))
	  (defmacro defun-curry (name (&rest args) &body body)
	    (let ((dc-lambda (gensym)) (rl (gensym)) (rl-copy (gensym)))
	      `(let (,dc-lambda)
		 (defun ,name ,args
		   (setq ,dc-lambda (list ,@args))
		   (unless (memq ',,spec-symb ,dc-lambda)
		     (return-from ,name ,@body))
		   (lambda (&rest ,rl &aux (,rl-copy (copy-tree ,rl)))
		     (if (/= (list-length ,rl) (count ',,spec-symb ,dc-lambda))
			 (error "wrong number of args"))
		     ,@(loop for i in args
			     collect `(if (eq ,i ',,spec-symb)
					  (setq ,i (pop ,rl-copy))))
		     ,@body))))))))
;;; - END -


CL-USER> (make-defun-curry)
DEFUN-CURRY

CL-USER> (defun-curry foo (a b c d) (list a b c d))
FOO

CL-USER> (setq f1 (foo ~ ~ 3 4) f2 (foo 1 ~ 3 ~))
#<Interpreted Function "DEFUN-CURRY FOO" {58549809}>

CL-USER> (funcall f1 'a 'b)
(A B 3 4)

CL-USER> (funcall f2 'a 'b)
(1 A 3 B)


Regards, Szymon.
From: Joerg Hoehle
Subject: Re: simple defun-curry without COMPILE (was: defun-curry finished.  (was: Useful (I hope) trick.))
Date: 
Message-ID: <uu0vpa2r3.fsf@users.sourceforge.net>
Szymon <···@bar.baz> writes:
> ;;; - CODE -
> (defmacro make-defun-curry (&key (spec-symb '~))
>   `(progn (defconstant ,spec-symb (gensym "~S~"))
> 	  (defmacro defun-curry (name (&rest args) &body body)
> 	    (let ((dc-lambda (gensym)) (rl (gensym)) (rl-copy (gensym)))
> 	      `(let (,dc-lambda)
> 		 (defun ,name ,args
> 		   (setq ,dc-lambda (list ,@args))
> 		   (unless (memq ',,spec-symb ,dc-lambda)
> 		     (return-from ,name ,@body))
> 		   (lambda (&rest ,rl &aux (,rl-copy (copy-tree ,rl)))
> 		     (if (/= (list-length ,rl) (count ',,spec-symb ,dc-lambda))
> 			 (error "wrong number of args"))
> 		     ,@(loop for i in args
> 			     collect `(if (eq ,i ',,spec-symb)
> 					  (setq ,i (pop ,rl-copy))))
> 		     ,@body))))))))
> ;;; - END -

I've only little time, so here are short remarks:

o there's one closing parenthesis too much

o defconstant is going to cause trouble when you repeatedly use the
same symbol.

Dod you want to choose the symbol so that you can learn about
doubly-nested backquotes? :-)

o rl-copy is superfluous. With POP, you don't side-effect the
list. You change only the variable.

o I liked the CASE -> lambda list distinction in your initial
version. Now, a run-time list-length test is inelegant IMHO.
Of course, you could hide that inside the POP loop.

o I haven't checked the consequences of dc-lambda being closed over.
What advantage do you believe it will give the code?

Regards,
	Jorg Hohle
Telekom/T-Systems Technology Center
From: Szymon
Subject: Re: simple defun-curry without COMPILE.
Date: 
Message-ID: <87r7qstjbx.fsf_-_@eva.rplacd.net>
Joerg Hoehle <······@users.sourceforge.net> writes:

Thanks for the reply.

> I've only little time, so here are short remarks:
> 
> o there's one closing parenthesis too much

Oh :)

> o defconstant is going to cause trouble when you repeatedly use the
> same symbol.

This is the feature... (I want this).

> Dod you want to choose the symbol so that you can learn about
> doubly-nested backquotes? :-)

:-)

> o rl-copy is superfluous. With POP, you don't side-effect the
> list. You change only the variable.

Ok.

> o I liked the CASE -> lambda list distinction in your initial
> version. Now, a run-time list-length test is inelegant IMHO.

Yes, but you want DEFUN-CURRY "in half screenful" ;) so if I had used CASE
it would have occupied more than about 12-14 lines ;)

Btw, CASE is justified only when I don't want to use &REST (I meant 'full'
(with implementation of lambda-list-keywords) defun-curry).

> ... o I haven't checked the consequences of dc-lambda being closed over.
> What advantage do you believe it will give the code?

If I remember correctly my intention was to correct the bug you pointed
out last time ;)

Regards, Szymon.
From: Szymon
Subject: Re: simple defun-curry without COMPILE.
Date: 
Message-ID: <8765818rq6.fsf@eva.rplacd.net>
Szymon <···@bar.baz> writes:

> > ... o I haven't checked the consequences of dc-lambda being closed over.
> > What advantage do you believe it will give the code?

Any :?)

> If I remember correctly my intention was to correct the bug you pointed
> out last time ;)

not true, sorry...
From: Szymon
Subject: Re: simple defun-curry with CASE.
Date: 
Message-ID: <871xip8ou2.fsf_-_@eva.rplacd.net>
Joerg Hoehle <······@users.sourceforge.net> writes:

> ... o I liked the CASE -> lambda list distinction in your initial
> version. Now, a run-time list-length test is inelegant IMHO.
> Of course, you could hide that inside the POP loop. ...


;;; - CODE -

(defmacro defun-curry (name (&rest args) &body body)                    ; 01
  (let ((rl (gensym)) (f (gensym)) xx)                                  ; 02
    `(flet ((,f ,args ,@body))                                          ; 03
       (defun ,name ,args                                               ; 04
         (unless (memq ~ (list ,@args))                                 ; 05
           (return-from ,name ,@body))                                  ; 06
         (case (count ~ (list ,@args))                                  ; 07
           ,@(loop for c from 1 upto (length args) collect              ; 08
                   `(,c #'(lambda                                       ; 09
                            (,@(setq xx                                 ; 10
                                     (loop repeat c collect (gensym)))  ; 11
                             &aux (,rl (list ,@xx))                     ; 12
                             ,@(loop for symb in args collect           ; 13
                                     `(,symb (if (eq ,symb ~)           ; 14
                                                 (pop ,rl) ,symb))))    ; 16
                        (,f ,@args)))))))))                             ; 17

;;; - END -


;;; SAMPLE MACROEXPANSION:

(defun-curry foo (a b c d) (list a b c d))

===>

(FLET ((#:G2972 (A B C D) (LIST A B C D)))
  (DEFUN FOO (A B C D)
    (UNLESS (MEMQ ~ (LIST A B C D)) (RETURN-FROM FOO (LIST A B C D)))

    (CASE (COUNT ~ (LIST A B C D))

      (1 #'(LAMBDA (#:G2973
                    &AUX (#:G2971 (LIST #:G2973))
                         (A (IF (EQ A ~) (POP #:G2971) A))
                         (B (IF (EQ B ~) (POP #:G2971) B))
                         (C (IF (EQ C ~) (POP #:G2971) C))
                         (D (IF (EQ D ~) (POP #:G2971) D)))
             (#:G2972 A B C D)))

      (2 #'(LAMBDA (#:G2974 #:G2975
                    &AUX (#:G2971 (LIST #:G2974 #:G2975))
                         (A (IF (EQ A ~) (POP #:G2971) A))
                         (B (IF (EQ B ~) (POP #:G2971) B))
                         (C (IF (EQ C ~) (POP #:G2971) C))
                         (D (IF (EQ D ~) (POP #:G2971) D)))
             (#:G2972 A B C D)))

      (3 #'(LAMBDA (#:G2976 #:G2977 #:G2978
                    &AUX (#:G2971 (LIST #:G2976 #:G2977 #:G2978))
                         (A (IF (EQ A ~) (POP #:G2971) A))
                         (B (IF (EQ B ~) (POP #:G2971) B))
                         (C (IF (EQ C ~) (POP #:G2971) C))
                         (D (IF (EQ D ~) (POP #:G2971) D)))
             (#:G2972 A B C D)))

      (4 #'(LAMBDA (#:G2979 #:G2980 #:G2981 #:G2982
                    &AUX (#:G2971 (LIST #:G2979 #:G2980 #:G2981 #:G2982))
                         (A (IF (EQ A ~) (POP #:G2971) A))
                         (B (IF (EQ B ~) (POP #:G2971) B))
                         (C (IF (EQ C ~) (POP #:G2971) C))
                         (D (IF (EQ D ~) (POP #:G2971) D)))
             (#:G2972 A B C D))))))


Regards, Szymon.