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/>
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.
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
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.
* 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
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
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