From: Karl A. Krueger
Subject: Recycling functions
Date: 
Message-ID: <d3u0db$gme$1@baldur.whoi.edu>
PCL mentions that 30% of uses of "recycling" functions (i.e. destructive
equivalents used for optimization) in the CLOCC codebase are of the
form:

	(setf foo (recycling-function ... foo ...))

It occurred to me that this could perhaps be a macro.  Just as SETQ was
once (SET (QUOTE ...)) it would seem to make sense to abbreviate this
common form.

My first try was:

	(defmacro setr (place operation &rest args)
		`(setf ,place (,operation ,place ,@args)))

... which lets you do (SETR FOO NCONC BAR) or (SETR FOO NREVERSE) for
instance.  Nor is it limited to recycling:  (SETR FOO CAR) makes sense;
or (SETR N + 5), although we have INCF for that.

This would seem to be analogous to the "modify in place" operators in
C-ish languages, e.g. += or Perl's .= for string collection:  being able
to say "foo += 5" is nicer than "foo = foo + 5".

However, there are lots of recycling functions where the argument being
mutated isn't the first argument, such as DELETE.  You don't want to
(SETF FOO (DELETE FOO 3)), you want to (SETF FOO (DELETE 3 FOO)).  And
sometimes you want to NCONC things onto the front of the list rather
than the end.

Now, of course, the obvious way to indicate where in the args list the
recycled "place" should go would be to simply put it there, and to make
clear which argument was the "place" by naming it.  Which is precisely
what the SETF syntax does.

So did I just disprove the usefulness of a macro akin to SETR?  :)

-- 
Karl A. Krueger <········@example.edu> { s/example/whoi/ }

From: M Jared Finder
Subject: Re: Recycling functions
Date: 
Message-ID: <or-dncb2wZGTDf_fRVn-uQ@speakeasy.net>
Karl A. Krueger wrote:
> PCL mentions that 30% of uses of "recycling" functions (i.e. destructive
> equivalents used for optimization) in the CLOCC codebase are of the
> form:
> 
> 	(setf foo (recycling-function ... foo ...))
> 
> It occurred to me that this could perhaps be a macro.  Just as SETQ was
> once (SET (QUOTE ...)) it would seem to make sense to abbreviate this
> common form.
> 
> My first try was:
> 
> 	(defmacro setr (place operation &rest args)
> 		`(setf ,place (,operation ,place ,@args)))
> 
> .... which lets you do (SETR FOO NCONC BAR) or (SETR FOO NREVERSE) for
> instance.  Nor is it limited to recycling:  (SETR FOO CAR) makes sense;
> or (SETR N + 5), although we have INCF for that.
> 
> This would seem to be analogous to the "modify in place" operators in
> C-ish languages, e.g. += or Perl's .= for string collection:  being able
> to say "foo += 5" is nicer than "foo = foo + 5".
> 
> However, there are lots of recycling functions where the argument being
> mutated isn't the first argument, such as DELETE.  You don't want to
> (SETF FOO (DELETE FOO 3)), you want to (SETF FOO (DELETE 3 FOO)).  And
> sometimes you want to NCONC things onto the front of the list rather
> than the end.
> 
> Now, of course, the obvious way to indicate where in the args list the
> recycled "place" should go would be to simply put it there, and to make
> clear which argument was the "place" by naming it.  Which is precisely
> what the SETF syntax does.
> 
> So did I just disprove the usefulness of a macro akin to SETR?  :)

SETF has the same problem; the mutating functions don't have any 
particular pattern to their name or argument lists.  To set VAR, the 
form (SETQ VAR VALUE) is used, to set (CAR VAR), the form (RPLACA VAR 
VALUE), and for (ELT SEQ), some implementation defined form is used.

SETF handles this by having a table of different expansions for each 
functional form it encounters.  This table is modified when you evaluate 
(DEFSETF ...) or (DEFUN (SETF ...) ...), as well as some other forms I 
don't know.  You should be able to support your SETR in the exact same way.

   -- MJF
From: Pascal Bourguignon
Subject: Re: Recycling functions
Date: 
Message-ID: <87d5st75jh.fsf@thalassa.informatimago.com>
"Karl A. Krueger" <········@example.edu> writes:
> However, there are lots of recycling functions where the argument being
> mutated isn't the first argument, such as DELETE.  You don't want to
> (SETF FOO (DELETE FOO 3)), you want to (SETF FOO (DELETE 3 FOO)).  And
> sometimes you want to NCONC things onto the front of the list rather
> than the end.
> 
> Now, of course, the obvious way to indicate where in the args list the
> recycled "place" should go would be to simply put it there, and to make
> clear which argument was the "place" by naming it.  Which is precisely
> what the SETF syntax does.
> 
> So did I just disprove the usefulness of a macro akin to SETR?  :)

Not at all. Add a DEFSETR to specify where the place must go.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Grace personified,
I leap into the window.
I meant to do that.
From: Karl A. Krueger
Subject: Re: Recycling functions
Date: 
Message-ID: <d3uob7$pho$1@baldur.whoi.edu>
Pascal Bourguignon <····@mouse-potato.com> wrote:
> "Karl A. Krueger" <········@example.edu> writes:
>> So did I just disprove the usefulness of a macro akin to SETR?  :)
> 
> Not at all. Add a DEFSETR to specify where the place must go.

Not exactly clean, but it does work:


(defparameter *setr-expanders* nil)

(defmacro setr (place operation &rest args)
  (let ((opfun (assoc operation *setr-expanders*)))
    (if (null opfun)  ; if we didn't get an expander, use the default
      `(setf ,place (,operation ,place ,@args)) 
      (let ((fun (cdr opfun)))
        `(setf ,place (,operation ,@(funcall fun place args)))))))

(defmacro defsetr (operation fun)
  `(push '(,operation . ,(symbol-function fun)) *setr-expanders*))

(defun one-arg-then-place (place args)
  (list (first args) place))

(defsetr delete one-arg-then-place)


; And a test:

(defparameter foo (list 'the 'monkey 'bit 'jim))
(setr foo delete 'jim)
(setr foo append '(llamas))

; foo -> (the monkey bit llamas)

-- 
Karl A. Krueger <········@example.edu> { s/example/whoi/ }
From: Kalle Olavi Niemitalo
Subject: Re: Recycling functions
Date: 
Message-ID: <87ekd863ej.fsf@Astalo.kon.iki.fi>
"Karl A. Krueger" <········@example.edu> writes:

> 	(setf foo (recycling-function ... foo ...))
>
> It occurred to me that this could perhaps be a macro.

Emacs Lisp has the callf and callf2 macros for cases where the
recycling-function takes foo as the first or second argument.
I guess these cover most destructive functions.

> 	(defmacro setr (place operation &rest args)
> 		`(setf ,place (,operation ,place ,@args)))

Note this evaluates the PLACE form two times.
To fix that, you need GET-SETF-EXPANSION.