From: Jonathan LF King
Subject: setf method for `if'
Date: 
Message-ID: <xndg0himniq.fsf@maxwell.math.ufl.edu>
Dear Cognoscenti:   Running Emacs + "cl.*", I found that 

    (setf (if in-vector-p
		    (aref *GEM-VECTOR* n)
		  (gethash n *GEM-HASH*) )
		jewel )

failed with message "No setf-method known for if".  
  Q1: Does real CL have such?

I superstitiously modified the "cl-macs.el"  `defsetf' for `elt' to produce:

    (defsetf if (boolean true-clause false-clause) (store)
      (list 'if boolean
	    (list 'setf true-clause store)
	    (list 'setf false-clause store)
	    ) )

This appears to work in the simple case above.  Q2: What is a
correct setf-method for `if', or is better that `if' not have
such?  Sincerely, -Jonathan

PS: Why do `aref' and `gethash' appear to ask for corresponding
arguments in opposite orders?
-- 
Prof. Jonathan LF King	  Mathematics dept, Univ. of Florida
<······@math.ufl.edu>,	  <http://www.math.ufl.edu/~squash/>

From: Barry Margolin
Subject: Re: setf method for `if'
Date: 
Message-ID: <mmfi6.15$LG2.3274@burlma1-snr2>
In article <···············@maxwell.math.ufl.edu>,
Jonathan LF King  <······@math.ufl.edu> wrote:
>Dear Cognoscenti:   Running Emacs + "cl.*", I found that 
>
>    (setf (if in-vector-p
>		    (aref *GEM-VECTOR* n)
>		  (gethash n *GEM-HASH*) )
>		jewel )
>
>failed with message "No setf-method known for if".  
>  Q1: Does real CL have such?
>
>I superstitiously modified the "cl-macs.el"  `defsetf' for `elt' to produce:
>
>    (defsetf if (boolean true-clause false-clause) (store)
>      (list 'if boolean
>	    (list 'setf true-clause store)
>	    (list 'setf false-clause store)
>	    ) )
>
>This appears to work in the simple case above.  Q2: What is a
>correct setf-method for `if', or is better that `if' not have
>such?  Sincerely, -Jonathan

Off the top of my head I can't think of a serious problem with the above
setf method.

>PS: Why do `aref' and `gethash' appear to ask for corresponding
>arguments in opposite orders?

Most functions that search for something in a collection have the thing
first and the collection second; e.g. MEMBER and FIND.  AREF needs to have
the position at the end so that it can handle any number of dimensions,
e.g. (aref array x y z).

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Tim Bradshaw
Subject: Re: setf method for `if'
Date: 
Message-ID: <nkjk86u8kra.fsf@tfeb.org>
Jonathan LF King <······@math.ufl.edu> writes:

> 
> This appears to work in the simple case above.  Q2: What is a
> correct setf-method for `if', or is better that `if' not have
> such?  Sincerely, -Jonathan
> 

As far as I can see your method would be OK -- you can't actually
define one in a real CL because you can't do that for symbols exported
by CL.  I feel kind of bad about the idea of one, but I can't actually
see what would be wrong with having one: perhaps someone else can.

> PS: Why do `aref' and `gethash' appear to ask for corresponding
> arguments in opposite orders?

One reason aref can't be the same order asgethash is (aref x a b c
...) for multi-dimensional arrays.  I don't know why GETHASH is in the
order it is -- it's backwards from things like GET, isn't it?
Probably it's that way because lots of implementations had it that
way?

--tim
From: Barry Wilkes
Subject: Re: setf method for `if'
Date: 
Message-ID: <87vgqemhyv.fsf@orton.bew.org.uk>
Tim Bradshaw <···@tfeb.org> writes:

> Jonathan LF King <······@math.ufl.edu> writes:
> 
> > 
> > This appears to work in the simple case above.  Q2: What is a
> > correct setf-method for `if', or is better that `if' not have
> > such?  Sincerely, -Jonathan
> > 
> 
> As far as I can see your method would be OK -- you can't actually
> define one in a real CL because you can't do that for symbols exported
> by CL.  I feel kind of bad about the idea of one, but I can't actually
> see what would be wrong with having one: perhaps someone else can.
> 
Is that correct? Looking at the hyperspec one of the examples it gives for
defsetf is :

 (defsetf subseq (sequence start &optional end) (new-sequence)
   `(progn (replace ,sequence ,new-sequence
                    :start1 ,start :end1 ,end)
           ,new-sequence))

which defines a setf for subseq. There didn't seem to be any mention in the
hyperspec of not being able to define a setf for symbols exported by CL. I
specifically tried the defsetf that the original poster gave in LispWorks, and
it didn't complain or warn at all.

Regards,

Barry.
From: Tim Bradshaw
Subject: Re: setf method for `if'
Date: 
Message-ID: <ey37l2ucjuf.fsf@cley.com>
* Barry Wilkes wrote:
> Is that correct? Looking at the hyperspec one of the examples it gives for
> defsetf is :

Yes, it is correct.  However I think it that implementations are not
required to actually notice this.  If you want to be conforming you
should define a package which shadows IF and then define the SETF
method there.

As for the example, I think that examples from the spec are just that,
they're not meant to define what it is possible to actually say.
SUBSEQ, for instance does actually have a SETF method defined by the
standard.

--tim
From: Hallvard B Furuseth
Subject: Re: setf method for `if'
Date: 
Message-ID: <HBF.20010213wxnt@bombur.uio.no>
Jonathan LF King <······@math.ufl.edu> writes:
> I superstitiously modified the "cl-macs.el"  `defsetf' for `elt' to produce:
> 
>     (defsetf if (boolean true-clause false-clause) (store)
>       (list 'if boolean
> 	    (list 'setf true-clause store)
> 	    (list 'setf false-clause store)
> 	    ) )
> 
> This appears to work in the simple case above.  Q2: What is a
> correct setf-method for `if', or is better that `if' not have
> such?  Sincerely, -Jonathan

It automatically has an incorrect one, at least in cmucl and clisp.

(get-setf-method '(if x y z))
->
	(#:g849 #:g850 #:g851) ;
	(x y z) ;
	(#:g852) ;
	(if #:g849 (setf #:g850 #1=#:g852) (setf #:g851 #1#)) ;
	(if #:g849 #:g850 #:g851)

-- 
Hallvard
From: Pekka P. Pirinen
Subject: Re: setf method for `if'
Date: 
Message-ID: <ixk86tw5jw.fsf@harlequin.co.uk>
Hallvard B Furuseth <············@usit.uio.no> writes:
> Jonathan LF King <······@math.ufl.edu> writes:
> > I superstitiously modified the "cl-macs.el" `defsetf' for `elt' to produce:
> > 
> >     (defsetf if (boolean true-clause false-clause) (store)
> >       (list 'if boolean
> > 	    (list 'setf true-clause store)
> > 	    (list 'setf false-clause store)
> > 	    ) )
> > 
> > This appears to work in the simple case above.  Q2: What is a
> > correct setf-method for `if', or is better that `if' not have
> > such?  Sincerely, -Jonathan

It's not quite perfect, but there's no deep reason why there couldn't
be one.  I think the basic requirement is only evaluating the correct
clause -- this is a little tricky in SETF.

> It automatically has an incorrect one, at least in cmucl and clisp.

That's because they made the same mistake as above: DEFSETF
automatically takes care of rebinding the subforms, so you can't use
it if you don't want to evaluate all the subforms.  As you show:

> (get-setf-method '(if x y z))
> ->
> 	(#:g849 #:g850 #:g851) ;
> 	(x y z) ;
> 	(#:g852) ;
> 	(if #:g849 (setf #:g850 #1=#:g852) (setf #:g851 #1#)) ;
> 	(if #:g849 #:g850 #:g851)

It might seem that it is enough just to move the evaluation of the
clauses into the setter and getter forms:

(define-setf-expander if (condition true-clause false-clause)
  (let ((store (gensym)) (flag (gensym)))
    (values `(,flag)
            `(,condition)
            `(,store)
            `(if ,flag
                 (setf ,true-clause ,store)
                 (setf ,false-clause ,store))
            `(if ,flag
                 ,true-clause
                 ,false-clause))))

This works in simple cases:

CL-USER 10> (get-setf-method '(if x y z))
(#:g810)
(x)
(#:g809)
(if #:g810 (setf y #:g809) (setf z #:g809))
(if #:g810 y z)

However, users of setf expanders can use both the getter and the
setter at the same time, or even multiply.  This results in
reevaluation of the clauses, e.g., in

CL-USER 15> (pprint (macroexpand '(rotatef a (if x (car y) (car z)))))
(let* ((#:g821 a) (#:g822 x) (#:g820 (if #:g822 (car y) (car z))))
  (setq a #:g820)
  (if #:g822 (setf (car y) #:g821) (setf (car z) #:g821))
  nil)

y or z is evaluated twice.  A good rule of thumb is that if you're
using SETF in a DEFINE-SETF-EXPANDER expansion, you're probably
ignoring some complication.  So, the correct version is:

(define-setf-expander if (condition true-clause false-clause &environment env)
  (multiple-value-bind (true-dummies true-vals true-stores true-set true-get)
      (get-setf-expansion true-clause env)
    (multiple-value-bind (false-dummies false-vals false-stores
                                        false-set false-get)
        (get-setf-expansion false-clause env)
      (let ((store (gensym)) (flag (gensym)))
        (values `(,flag ,@true-dummies ,@false-dummies)
                `(,condition
                  ,@(mapcar #'(lambda (v) `(if ,flag ,v)) true-vals)
                  ,@(mapcar #'(lambda (v) `(if ,flag nil ,v)) false-vals))
                `(,store)
                `(if ,flag
                     (let ((,(car true-stores) ,store)) ,true-set)
                     (let ((,(car false-stores) ,store)) ,false-set))
                `(if ,flag ,true-get ,false-get))))))

This works with ROTATEF:

CL-USER 23> (pprint (macroexpand '(rotatef a (if x (car y) (car z)))))
(let* ((#:g892 a)
       (#:g893 x)
       (#:g889 (if #:g893 y))
       (#:g891 (if #:g893 nil z))
       (#:g886 (if #:g893 (car #:g889) (car #:g891))))
  (setq a #:g886)
  (if #:g893
      (let ((#:g888 #:g892)) (system::%rplaca #:g889 #:g888))
    (let ((#:g890 #:g892)) (system::%rplaca #:g891 #:g890)))
  nil)

Since IF is single-valued, the SETF does not need to support multiple
store variables.
-- 
Pekka P. Pirinen
Adaptive Memory Management Group, Harlequin Limited
In cyberspace, everybody can hear you scream.  - Gary Lewandowski