Subject: Re: Curry (was Re: Elegant solution asked)
From: Erik Naggum <erik@naggum.no>
Date: 1997/03/12
Newsgroups: comp.lang.lisp
Message-ID: <3067167916417343@naggum.no>


* Jason Trenouth
| > (defun curry (fn &rest curried-args)
| >   (setq curried-args (copy-list curried-args))	
| >   #'(lambda (&rest args)
| >       (apply fn (append curried-args args))))
| 
| APPEND is already copies the list.

`append' would copy curried-args, but not args.  I believe this was the
issue, since you take pains to copy the first argument list (as I believe I
did first in this discussion and everybody just copied).

I thought it was necessary to copy the rest list, as I recall having read
that the trivial definition of `list' would not be conforming:

    (defun list (&rest args)
      args)

however, I was in error.  there is no mention of stack-allocating rest
lists in ANSI X3.226, except under the `dynamic-extent' declaration, with
which it may be requested specially.  under `apply', we find the following:

    When the function receives its arguments via &rest, it is permissible
    (but not required) for the implementation to bind the rest parameter to
    an object that shares structure with the last argument to apply.
    Because a function can neither detect whether it was called via apply
    nor whether (if so) the last argument to apply was a constant,
    conforming programs must neither rely on the list structure of a rest
    list to be freshly consed, nor modify that list structure.

the X3J13 issue REST-LIST-ALLOCATION contains a lot of discussion, see
<URL:http://www.harlequin.com/books/HyperSpec/Issues/iss297-writeup.html>.
CLtL2 includes the following about this issue (taken from the published TeX
sources).

    \begin{new}
    X3J13 voted in January 1989
    \issue{REST-LIST-ALLOCATION}
    to clarify that if a function has a {\it rest} parameter and is called
    using \cd{apply}, then the list to which the {\it rest} parameter is
    bound is permitted, but not required, to share top-level list structure
    with the list that was the last argument to \cd{apply}.  Programmers
    should be careful about performing side effects on the top-level list
    structure of a {\it rest} parameter.

    This was the result of a rather long discussion within X3J13 and the
    wider Lisp community.  To set it in its historical context, I must
    remark that in Lisp Machine Lisp the list to which a {\it rest}
    parameter was bound had only dynamic extent; this in conjunction with
    the technique of ``cdr-coding'' permitted a clever stack-allocation
    technique with very low overhead.  However, the early designers of
    Common Lisp, after a great deal of debate, concluded that it was
    dangerous for cons cells to have dynamic extent; as an example, the
    ``obvious'' definition of the function \cd{list}
    \begin{lisp}
    (defun list (\&rest x) x)
    \end{lisp}
    could fail catastrophically.  Therefore the first edition simply
    implied that the list for a {\it rest} parameter, like all other lists,
    would have indefinite extent.  This still left open the flip side of
    the question, namely, Is the list for a {\it rest} parameter guaranteed
    fresh?  This is the question addressed by the X3J13 vote.  If it is
    always freshly consed, then it is permissible to destroy it, for
    example by giving it to \cd{nconc}.  However, the requirement always to
    cons fresh lists could impose an unacceptable overhead in many
    implementations.  The clarification approved by X3J13 specifies that
    the programmer may not rely on the list being fresh; if the function
    was called using \cd{apply}, there is no way to know where the list
    came from.
    \end{new}

the index to issues lists this as having been voted MAY-SHARE, that is:

    (defvar *my-list* '(a b c))
    (defun foo (&rest x) (eq x *my-list*))
    (apply #'foo *my-list*) => implementation-dependent

in any case, it seems that the need to copy the top-level list structure of
the rest list is not necessary unless the list structure will be modified,
which we don't.  for reference, this is the function definition from Paul
Graham's ANSI Common Lisp.  his Dylan function buildrs go like this (from
figure 6.2, page 110),

    (defun compose (&rest fns)
      (destructuring-bind (fn1 . rest) (reverse fns)
	#'(lambda (&rest args)
	    (reduce #'(lambda (v f) (funcall f v))
		    rest
		    :initial-value (apply fn1 args)))))

    (defun disjoin (fn &rest fns)
      (if (null fns)
	  fn
	  (let ((disj (apply #'disjoin fns)))
	    #'(lambda (&rest args)
		(or (apply fn args) (apply disj args))))))

    (defun conjoin (fn &rest fns)
      (if (null fns)
	  fn
	  (let ((conj (apply #'conjoin fns)))
	    #'(lambda (&rest args)
		(and (apply fn args) (apply conj args))))))

    (defun curry (fn &rest args)
      #'(lambda (&rest args2)
	  (apply fn (append args args2))))

    (defun rcurry (fn &rest args)
      #'(lambda (&rest args2)
	  (apply fn (append args2 args))))

    (defun always (x) #'(lambda (&rest args) x))

#\Erik
-- 
how to beat Microsoft, death, and poverty: in July 1994, there were more
references to my name (3039) in gopherspace than to Microsoft (2557), death
(2530), and poverty (2410).  (http://veronica.sonoma.edu:8001/top1000.html)