From: Francois-Rene Rideau
Subject: multiple-value version of define-modify-macro ?
Date: 
Message-ID: <87u1wceozb.fsf@Samaris.tunes.org>
Some friends have asked me to implement the double-fixnum arithmetics
trick (as suggested by Erik Naggum, among other people) in my md5.lisp.
So as to do the Right Thing(tm), I've decided to write a set of generic
double-fixnum (or maybe even multiple-fixnum) arithmetic macros.
Then, I realize that I can't seem to be able to do the Right Thing,
because the reliable equivalent of INCF for double-fixnum arithmetics
is not defineable as a macro.

When you want to define an INCF-style macro, you can use
DEFINE-MODIFY-MACRO, that allows to define macros that can
read a place, do stuff, and write back to the same place;
the trick of course being that the form that defines the place
might contain side-effects, which side-effects must be evaluated
but once.

Because of this essential property, DEFINE-MODIFY-MACRO is not
implementable in the terms of the rest of CL (not unless you
write a full-fledged code-walker, which is an "interesting" challenge
per se, that becomes hellish if you ever need to grok any
language extension). Similarly, a multiple-value version of it
is not definable.

I mean something like
(define-values-modify-macro df-incf ((lo1 hi1) lo2 hi2)
   df-add "incf for double-fixnum arithmetics")
where (df-add lo1 hi1 lo2 hi2) returns the sum of lo1 hi1 and lo2 hi2
as a pair of values.

I can of course define a version that works when lo1 and hi1
have no side-effects, but loses when they do.

Have other people been confronted to this problem?
What is the right solution?
Which CL implementation have first-class or second class places?
(aka locatives?) What are the documented protocols to manipulate them?

Yours freely,

[ Fran�ois-Ren� �VB Rideau | Reflection&Cybernethics | http://fare.tunes.org ]
[  TUNES project for a Free Reflective Computing System  | http://tunes.org  ]
The only thing necessary for the triumph of evil is for good men to do nothing.
	-- Edmund Burke

From: Kent M Pitman
Subject: Re: multiple-value version of define-modify-macro ?
Date: 
Message-ID: <sfwhescele5.fsf@world.std.com>
Francois-Rene Rideau <···········@tunes.org> writes:

> ... When you want to define an INCF-style macro, you can use
> DEFINE-MODIFY-MACRO, that allows to define macros that can
> read a place, do stuff, and write back to the same place;
> the trick of course being that the form that defines the place
> might contain side-effects, which side-effects must be evaluated
> but once.
> 
> Because of this essential property, DEFINE-MODIFY-MACRO is not
> implementable in the terms of the rest of CL (not unless you
> write a full-fledged code-walker, which is an "interesting" challenge
> per se, that becomes hellish if you ever need to grok any
> language extension). Similarly, a multiple-value version of it
> is not definable. ...

You want to read:

 SETF
 5.1 Generalized Reference
  (pay special attention to 5.1.1.2 Setf Expansions)
 DEFINE-SETF-EXPANDER
 GET-SETF-EXPANSION

I'm pretty sure DEFINE-MODIFY-MACRO is just a meta-macro based on
these other tools.  Though I admit I'm in a hurry as I write this so
I didn't really read your problem description in depth, so I'm counting
on someone else to correct me if I spazzed here.

It may take a few minutes to figure out the setf expansion things, but
once you do, you'll find they're mostly just an exercise in slightly
baroque and elaborate bookkeeping, but are quite useful and are not
really very conceptually hard...
From: Francois-Rene Rideau
Subject: Re: multiple-value version of define-modify-macro ?
Date: 
Message-ID: <87n124eg6c.fsf@Samaris.tunes.org>
Kent M Pitman <······@world.std.com> writes:
> You want to read:
> 
>  SETF
>  5.1 Generalized Reference
>   (pay special attention to 5.1.1.2 Setf Expansions)
>  DEFINE-SETF-EXPANDER
>  GET-SETF-EXPANSION
>
I had gone over this section much too quickly.
Sorry about the stupid question.
Thanks a lot for pointing me to the right place!

And thanks a lot to Tim Moore for his code;
that I'll try to grok tonight (or tomorrow).

[ Fran�ois-Ren� �VB Rideau | Reflection&Cybernethics | http://fare.tunes.org ]
[  TUNES project for a Free Reflective Computing System  | http://tunes.org  ]
You cannot teach a man anything; you can only help him find it for himself.
	-- Galileo Galilei (?)
From: Tim Moore
Subject: Re: multiple-value version of define-modify-macro ?
Date: 
Message-ID: <9rvii9$261$0@216.39.145.192>
In article <··············@Samaris.tunes.org>, "Francois-Rene Rideau"
<···········@tunes.org> wrote:

> When you want to define an INCF-style macro, you can use
> DEFINE-MODIFY-MACRO, that allows to define macros that can read a place,
> do stuff, and write back to the same place; the trick of course being
> that the form that defines the place might contain side-effects, which
> side-effects must be evaluated but once.
> Because of this essential property, DEFINE-MODIFY-MACRO is not
> implementable in the terms of the rest of CL (not unless you write a
> full-fledged code-walker, which is an "interesting" challenge per se,
> that becomes hellish if you ever need to grok any language extension).

Presumably "place" means a place settable for setf.  In that case, the
only reason your assertion would be true is if GET-SETF-EXPANSION for
that place lies; perhaps the reader form has side effects.  Otherwise,
DEFINE-MODIFY-MACRO can be written in portable ANSI CL (ignoring gensym
issues):

(defmacro dmm (name lambda-list function)
  `(defmacro ,name (place ,@lambda-list &environment env)
     (multiple-value-bind (vars vals store-vars writer-form reader-form)
	 (get-setf-expansion place env)
       `(let* (,@(mapcar #'list vars vals)
	       ,@store-vars)
	  (multiple-value-setq ,store-vars 
	    (,',function ,reader-form ,,@lambda-list)) 
	  ,writer-form
	  (values ,@store-vars)))))



> Similarly, a multiple-value version of it is not definable.
> I mean something like
> (define-values-modify-macro df-incf ((lo1 hi1) lo2 hi2)
>    df-add "incf for double-fixnum arithmetics")
> where (df-add lo1 hi1 lo2 hi2) returns the sum of lo1 hi1 and lo2 hi2 as
> a pair of values.
This does what you want (not what you specified :)

(defmacro define-values-modify-macro (name val-vars lambda-list function)
  (let ((env (gensym "ENV")))
    `(defmacro ,name (,@val-vars ,@lambda-list &environment ,env)
       (multiple-value-bind (vars vals store-vars writer-form reader-form)
	   (get-setf-expansion `(values ,,@val-vars) ,env)
	 (let ((val-temps (mapcar #'(lambda (temp) (gensym (symbol-name temp)))
				  ',val-vars)))
	   `(let* (,@(mapcar #'list vars vals)
		   ,@store-vars)
	      (multiple-value-bind ,val-temps ,reader-form
		(multiple-value-setq ,store-vars 
		  (,',function ,@val-temps ,,@lambda-list)))
	      ,writer-form
	      (values ,@store-vars)))))))

> I can of course define a version that works when lo1 and hi1 have no
> side-effects, but loses when they do.  Have other people been confronted
> to this problem? What is the right solution?
Tim
From: Pierre R. Mai
Subject: Re: multiple-value version of define-modify-macro ?
Date: 
Message-ID: <874roc7h4z.fsf@orion.bln.pmsf.de>
Francois-Rene Rideau <···········@tunes.org> writes:

> When you want to define an INCF-style macro, you can use
> DEFINE-MODIFY-MACRO, that allows to define macros that can
> read a place, do stuff, and write back to the same place;
> the trick of course being that the form that defines the place
> might contain side-effects, which side-effects must be evaluated
> but once.
> 
> Because of this essential property, DEFINE-MODIFY-MACRO is not
> implementable in the terms of the rest of CL (not unless you
> write a full-fledged code-walker, which is an "interesting" challenge
> per se, that becomes hellish if you ever need to grok any
> language extension). [...]

All of those claims are trivially false.  Individual INCF- or
PUSH-style macros _are_ definable without DEFINE-MODIFY-MACRO, and
D-M-M itself is also definable in terms of the rest of CL, without
such silly things like code-walkers.

For the first item, take a look at get-setf-expansion, and for the
second, well, below is the definition of D-M-M from the CMU CL source
code (which is in the public domain, and hence shouldn't pose
licensing issues or such other silly things), and the only non-ANSI
thing in there is the call to memq, which can be replaced with member
in this instance, and the fact that we still call get-setf-expansion
under its obsolete (in the non-ANSI meaning of the word) name of
get-setf-method.

And any user can trivially look at the expansion of d-m-m to see
whether it needs any non-CL tricks, by doing

(macroexpand-1 '(define-modify-macro incf (&optional (delta 1)) +))

which yields in CMU CL:

(defmacro incf (#:g899 &optional (delta 1) &environment #:g898)
  nil
  (multiple-value-bind
      (common-lisp::dummies common-lisp::vals common-lisp::newval
       common-lisp::setter common-lisp::getter)
      (get-setf-method #:g899 #:g898)
    (do ((common-lisp::d common-lisp::dummies (cdr common-lisp::d))
         (common-lisp::v common-lisp::vals (cdr common-lisp::v))
         (common-lisp::let-list nil
                                (cons
                                 (list (car common-lisp::d)
                                       (car common-lisp::v))
                                 common-lisp::let-list)))
        ((null common-lisp::d)
         (push
          (list (car common-lisp::newval) (list '+ common-lisp::getter delta))
          common-lisp::let-list)
         `(let* (common-lisp::backq-comma (nreverse common-lisp::let-list))
            ,common-lisp::setter)))))

Nothing that is necessarily non-ANSI CL stuff in there.

All in all I would appreciate it very much, if you would refrain from
leaping to silly conclusions from problems that are only caused by
your lack of knowledge and understanding of the language.  Why not
simply ask for help when you encounter problems, without the silly
claims?

> language extension). Similarly, a multiple-value version of it
> is not definable.
>
> I mean something like
> (define-values-modify-macro df-incf ((lo1 hi1) lo2 hi2)
>    df-add "incf for double-fixnum arithmetics")
> where (df-add lo1 hi1 lo2 hi2) returns the sum of lo1 hi1 and lo2 hi2
> as a pair of values.

I'm only doing this by hand for the df-addf case, since I don't see
enough utility in a d-v-m-m facility to invest any more effort into
this:

(defmacro df-addf ((lo-place hi-place) delta-lo delta-hi &environment env)
  (multiple-value-bind (lo-dummies lo-vals lo-newval lo-setter lo-getter)
      (get-setf-expansion lo-place env)
    (multiple-value-bind (hi-dummies hi-vals hi-newval hi-setter hi-getter)
        (get-setf-expansion hi-place env)
      (loop for dummy-var in (append lo-dummies hi-dummies)
            for dummy-val in (append lo-vals hi-vals)
            collect `(,dummy-var ,dummy-val) into bindings
            finally
            (return
              `(let* (,@bindings)
                 (multiple-value-bind (,(car lo-newval) ,(car hi-newval))
                     (df-add ,lo-getter ,hi-getter ,delta-lo ,delta-hi)
                   (values ,lo-setter ,hi-setter))))))))

This should work as specified, e.g.

* (let ((stuff (copy-seq #(0 4 5 10 20)))
      (index 0))
  (multiple-value-prog1
      (df-addf ((svref stuff (incf index)) (svref stuff (incf index)))
               (svref stuff (incf index)) (svref stuff (incf index)))
    (print stuff)))

#(0 14 25 10 20) 
14
25

> Have other people been confronted to this problem?
> What is the right solution?

When I'm confronted with a problem, I find that reading the fine
standard (often in the form of the HyperSpec) will often guide me
towards the right solution.

> Which CL implementation have first-class or second class places?
> (aka locatives?) What are the documented protocols to manipulate them?

Most current CL implementations on stock hardware don't support
locatives, since they exhibit non-trivial performance problems.

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Francois-Rene Rideau
Subject: Re: multiple-value version of define-modify-macro ?
Date: 
Message-ID: <87elngf6rn.fsf@Samaris.tunes.org>
"Pierre R. Mai" <····@acm.org> writes:
> All of those claims are trivially false.
Indeed. I've been as stupid as ignorant, and I apologize for it.

> And any user can trivially look at the expansion of d-m-m to see
> whether it needs any non-CL tricks, by doing
>
> (macroexpand-1 '(define-modify-macro incf (&optional (delta 1)) +))
>
Great trick.

> All in all I would appreciate it very much, if you would refrain from
> leaping to silly conclusions from problems that are only caused by
> your lack of knowledge and understanding of the language.  Why not
> simply ask for help when you encounter problems, without the silly
> claims?
I will try to.

[ Fran�ois-Ren� �VB Rideau | Reflection&Cybernethics | http://fare.tunes.org ]
[  TUNES project for a Free Reflective Computing System  | http://tunes.org  ]
  It is better to keep your mouth shut and be thought a fool,
	    than to open it and remove all doubt.