Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

UPDATE, UPDATE*

3 views
Skip to first unread message

Kaz Kylheku

unread,
Nov 4, 2009, 12:42:24 AM11/4/09
to
Macro UPDATE, UPDATE*:

Syntax:

update ( {(var place)}* ) declaration* form* => result*

update* ( {(var place)}* ) declaration* form* => result*

Arguments and Values:

var -- a symbol

place -- a place

declaration -- a declare expression; not evaluated

form -- a form

results -- the multiple values returned by the last form

Description:

update establishes an environment, for the evaluation of the given forms, in
which var denotes the corresponding place. Prior to the evaluation of the
forms, var is initialized by accessing the value stored in place. The forms
may assign a new value to var. After the execution of the forms, the updated
value of var is stored back into the place which val denotes. This is all
done in such a way that place is evaluated only once.

If place is a multiple value place resulting from VALUES, then the
corresponding var denotes the first subform.

When producing the initial values for the variables, the update macro
evaluates the places (in left to right order) in an environment in which the
variables do not yet have bindings. The variables are then bound in parallel
to the resulting values.

The update* macro is different from update in this regard; it evaluates each
place in an environment in which the prior variables already have a binding
from the evaluaton of the prior places.

If the evaluation of forms is terminated by a dynamic control transfer, then
the update of places does not take place.

Places are updated with new values in left to right order by means of store
forms. If any store form initiates a dynamic control transfer, that store,
and any subsequent stores, will not take place.

The multiple values produced by the last form are returned.

Reference implementation:

(eval-when (:compile-toplevel :load-toplevel :execute)

(defun collect-expansion-pieces (var-place-forms)
(loop for (var place) in var-place-forms
collect `(,var ,@(multiple-value-list (get-setf-expansion place)))))

(defun separate-decls (body)
(let* ((forms (member-if (lambda (form)
(not (and (consp form)
(eq (car form) 'declare))))
body))
(decls (ldiff body forms)))
(values decls forms)))

(defun expand-update (let-op var-place-forms body)
(multiple-value-bind (decls forms) (separate-decls body)
(loop for (var dummies vals news setter getter)
in (collect-expansion-pieces var-place-forms)
collecting var into all-vars
collecting dummies into all-dummies-sets
collecting vals into all-vals-sets
collecting (car news) into all-news
collecting setter into all-setters
collecting getter into all-getters
finally
(return `(,let-op (,@(mapcan (lambda (dummies vals var getter)
`(,@(mapcar #'list dummies vals)
(,var ,getter)))
all-dummies-sets all-vals-sets
all-vars all-getters))
,@decls
(symbol-macrolet (,@(mapcar #'list all-news all-vars))
(multiple-value-prog1
(progn ,@forms)
,@all-setters)))))))

(defmacro update ((&rest var-place-forms) &body body)
(expand-update 'let var-place-forms body))

(defmacro update* ((&rest var-place-forms) &body body)
(expand-update 'let* var-place-forms body)))

Kaz Kylheku

unread,
Nov 4, 2009, 1:39:43 AM11/4/09
to
On 2009-11-04, Kaz Kylheku <kkyl...@gmail.com> wrote:
> Macro UPDATE, UPDATE*:

...

> If place is a multiple value place resulting from VALUES, then the
> corresponding var denotes the first subform.

The additional places
denoted by other subforms are ignored.

...

> Reference implementation:

...

Bugfix:

> collecting (car news) into all-news

> - collecting setter into all-setters

+ collecting (if (cdr news) (second setter) setter) into all-setters

The assumption is that if two or more new-value variables emerge for a form
from GET-SETF-EXPANSION, i.e. (cdr news) is not nil, then the form must be
(VALUES ...), and so the setter form is also based on VALUES. We don't want
the entire setter in that case, but just its subform which updates the first
value. The assumption is that we ca pull that out using SECOND because the
setter looks like (VALUES (SETF ...) (SETF ...)).

But it seems nonportable. 5.1.2.3 says

The storing form in the setf expansion of values returns as multiple
values[2] the values of the store variables in step 2. That is, the
number of values returned is the same as the number of place forms.
This may be more or fewer values than are produced by the values-form

It is pinned down what the storing form returns, but not what it looks
like, whereas the above hack depends on what the form looks like.

Looks like I will have to earnestly generate variables for the unused places,
capture their values, etc.

Kaz Kylheku

unread,
Nov 4, 2009, 1:49:12 AM11/4/09
to
On 2009-11-04, Kaz Kylheku <kkyl...@gmail.com> wrote:
> On 2009-11-04, Kaz Kylheku <kkyl...@gmail.com> wrote:
>> Macro UPDATE, UPDATE*:
>
> ...
>
>> If place is a multiple value place resulting from VALUES, then the
>> corresponding var denotes the first subform.
>
> The additional places
> denoted by other subforms are ignored.

No way, this is all stupid. Let values-places be erroneous in this construct.


>
>> Reference implementation:
>
> ...
>
> Bugfix:
>
>> collecting (car news) into all-news
>> - collecting setter into all-setters
>
> + collecting (if (cdr news) (second setter) setter) into all-setters

(when (cdr news) (error ...))

vippstar

unread,
Nov 4, 2009, 10:02:57 AM11/4/09
to
On Nov 4, 7:42 am, Kaz Kylheku <kkylh...@gmail.com> wrote:
> Macro UPDATE, UPDATE*:
<snip CLHS-like documentation>

Hi Kaz, can you provide some examples for these macros?

Kaz Kylheku

unread,
Nov 4, 2009, 5:23:56 PM11/4/09
to
On 2009-11-04, vippstar <vipp...@gmail.com> wrote:

Here is one.

Suppose you have three complicated expressions which all denote places.

You want to access these places and then compute new values for all three
places based on the prior values, but only access the places once.

Here is the kick: there are dependencies; the new values are functions
of the old.

(update ((a (complicated-place-a))
(b (complicated-place-b))
(c (complicated-place-c)))
(psetf a (function-one a b c)
b (function-two a b c)
c (function-three a b c)))

With update, we can compute each new value as an arbitrary function of the
prior values of all of the places, while evaluating each of the three
place expressions just once.

If we define the functions like this:

(function-one a b c) -> b
(function-two a b c) -> c
(function-three a b c) -> a

then we get the same effect as:

(rotatef (complicated-place-a) (complicated-place-b) (copmlicated-place-c))

Here is an impelmentation of ROTATEF(*) based on UPDATE, which shows that
with UPDATE, you no longer have to know how to use GET-SETF-EXPANSION.
(But you have to generate your own gensyms for the update variables!)

(defmacro my-rotatef (&rest forms)
(if forms
(let ((temps (loop for x in forms collecting (gensym))))
`(update (,@(mapcar #'list temps forms))
(psetf ,@(mapcan #'list temps (rest temps))
,@(last temps) ,(first temps))))))

[2]> (macroexpand '(my-rotatef))
NIL ;
T
[3]> (macroexpand '(my-rotatef a))
(UPDATE ((#:G3111 A)) (PSETF #:G3111 #:G3111)) ;
T
[4]> (macroexpand '(my-rotatef a b))
(UPDATE ((#:G3112 A) (#:G3113 B)) (PSETF #:G3112 #:G3113 #:G3113 #:G3112)) ;
T
[5]> (macroexpand '(my-rotatef a b c d e))
(UPDATE ((#:G3114 A) (#:G3115 B) (#:G3116 C) (#:G3117 D) (#:G3118 E))
(PSETF #:G3114 #:G3115 #:G3115 #:G3116 #:G3116 #:G3117 #:G3117 #:G3118 #:G3118
#:G3114)) ;
T

---
* A crippled ROTATEF which doesn't support multiple value places, and
so cannot do (my-rotatef (values a b) (values c d) (values e f)).

budden

unread,
Nov 5, 2009, 3:55:08 AM11/5/09
to
Hi!
Didn't test it yet. Looks like a nice work. What is wrong?
1. Name is wrong. "Update" is a very general word. It is very
likely that everyone uses the name already in her library.
So name clashes would occur. E.g. I know about ap5:update
and I use it sometimes. So, people would rename your macro
in their code and this is bad.
2. Did you really forget &env argument?

Rob Warnock

unread,
Nov 5, 2009, 7:07:18 AM11/5/09
to
budden <budde...@mail.ru> wrote:
+---------------

| 1. Name is wrong. "Update" is a very general word. It is very
| likely that everyone uses the name already in her library.
| So name clashes would occur. ...
+---------------

WITH-PLACES might be a good choice, by analogy to WITH-SLOTS
or WITH-ACCESSORS.


-Rob

-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607

budden

unread,
Nov 5, 2009, 7:29:42 AM11/5/09
to
> WITH-PLACES might be a good choice, by analogy to WITH-SLOTS
> or WITH-ACCESSORS.
I was thinking of "WITH-PLACES" too. I vote.

Madhu

unread,
Nov 5, 2009, 8:03:25 AM11/5/09
to
* (Rob Warnock) <1e6dnfhzaY9rIm_X...@speakeasy.net> :
Wrote on Thu, 05 Nov 2009 06:07:18 -0600:

| budden <budde...@mail.ru> wrote:
| +---------------
| | 1. Name is wrong. "Update" is a very general word. It is very
| | likely that everyone uses the name already in her library.
| | So name clashes would occur. ...
| +---------------
|
| WITH-PLACES might be a good choice, by analogy to WITH-SLOTS
| or WITH-ACCESSORS.

Or SYMBOL-MACROLETF, As Kalle called it in his 2005 CLL post

See <http://paste.lisp.org/display/2992#1>

,----
| From: Kalle Olavi Niemitalo <k...@iki.fi>
| Subject: Re: ANSI compliance of check-type in various lisps
| Date: Tue, 15 Feb 2005 11:38:51 +0200
| Organization: Oulun Puhelin Oyj - Baana
| Message-ID: <878y5q5...@Astalo.kon.iki.fi>
|------------------------.fsf @ astalo
`----

--
Madhu

budden

unread,
Nov 5, 2009, 9:11:04 AM11/5/09
to
symbol-macroletf is even better.

Kaz Kylheku

unread,
Nov 5, 2009, 11:56:16 AM11/5/09
to
On 2009-11-05, budden <budde...@mail.ru> wrote:
> 2. Did you really forget &env argument?

That's a bug; good catch.

Kaz Kylheku

unread,
Nov 5, 2009, 5:37:22 PM11/5/09
to
On 2009-11-05, Rob Warnock <rp...@rpw3.org> wrote:
> budden <budde...@mail.ru> wrote:
> +---------------
>| 1. Name is wrong. "Update" is a very general word. It is very
>| likely that everyone uses the name already in her library.
>| So name clashes would occur. ...
> +---------------
>
> WITH-PLACES might be a good choice, by analogy to WITH-SLOTS
> or WITH-ACCESSORS.

WITH-SLOTS is a not a great analogy because under WITH-SLOTS, we really are
working with the actual slots by means of local aliases. Not so in UPDATE; the
local bindings are really just variables. Assigning to a variable does not
instantly change the corresponding place. There is still justification for
WITH, based on similarity to things like WITH-MUTEX-LOCK, WITHOUT-INTERRUPTS,
where there is some hidden effect inserted before and after the code.

I vote for UPDATEF.

Vassil Nikolov

unread,
Nov 5, 2009, 10:16:45 PM11/5/09
to

On Thu, 5 Nov 2009 22:37:22 +0000 (UTC), Kaz Kylheku <kkyl...@gmail.com> said:
> ...
> I vote for UPDATEF.

Hmmm, it doesn't just update, it also retrieves; READ-WRITE-F is
ugly, though, perhaps something with a "modify" root (to match
DEFINE-MODIFY-MACRO)?

---Vassil.


--
baguetton, n.
a newly discovered particle that can travel in time in the vicinity
of large experimental facilities and cause faults in them (cf. boson)

Kaz Kylheku

unread,
Nov 6, 2009, 1:16:45 AM11/6/09
to
On 2009-11-06, Vassil Nikolov <vnik...@pobox.com> wrote:
>
> On Thu, 5 Nov 2009 22:37:22 +0000 (UTC), Kaz Kylheku <kkyl...@gmail.com> said:
>> ...
>> I vote for UPDATEF.
>
> Hmmm, it doesn't just update, it also retrieves; READ-WRITE-F is

Not necessarily. In the abstract semantics

(update ((a place)) (setf a 42))

accesses place, stores it in a, allows a to be modified, and then
stores the value a back into place. But actually, the prior access
can be optimized away. The variable A is not live on entry into the body,
because the first use of A is to clobber it. Since A is not live,
place need not be accessed to retrieve the value for A.

The construction, in this case, expresses that A denotes PLACE,
and that PLACE is modified with a value which is not computed by
accessing the prior value of PLACE.

> ugly, though, perhaps something with a "modify" root (to match
> DEFINE-MODIFY-MACRO)?

The word update usually implies that the new value is related to the old one
(though possibly without obtaining the old value from the place being
updated).

Example of update implying a read: ``cvs update'' (incorporates local changes
by merging, rather than blindly replacing the local files with fresh
checkouts). Sometimes a read-modify-write is called an update.
In C programming, it is sometims said that the fopen "w+" mode opens
a file for update: both reading and writing.

Examples of ``update'' not implying prior read: the controller object, updates
the model, which updates the display; or: the local replica of the database is
updated from the master server.

The word modify, on the other hand, does not imply at all that the new value
is related to the old. If you initialize some storage to all zeros, that's a
modification, but probably won't be called an update.

If anything, ``modify'' places even less emphasis on the possibility of prior
access than ``update''.

Nicolas Neuss

unread,
Nov 6, 2009, 3:05:30 AM11/6/09
to
Kaz Kylheku <kkyl...@gmail.com> writes:

> WITH-SLOTS is a not a great analogy because under WITH-SLOTS, we
> really are working with the actual slots by means of local aliases.
> Not so in UPDATE; the local bindings are really just
> variables. Assigning to a variable does not instantly change the
> corresponding place.

So this is the difference between your UPDATE and the SYMBOL-MACROLETF
which Madhu refered to and which works directly with the places, no?
When should I use which? Does it make a difference at all, given a
Sufficiently Smart Compiler?

Nicolas

Nicolas Neuss

unread,
Nov 6, 2009, 3:45:56 AM11/6/09
to
Nicolas Neuss <last...@math.uni-karlsruhe.de> writes:

Hmm, thinking a little more about it the difference is clear. But I am
not yet convinced that I would like code depending on this difference.

Nicolas

Madhu

unread,
Nov 7, 2009, 8:16:19 PM11/7/09
to

* Nicolas Neuss <87my30v...@ma-patru.mathematik.uni-karlsruhe.de> :
Wrote on Fri, 06 Nov 2009 09:45:56 +0100:

I don't think do you have a valid use case for UPDATE/UPDATEF*. The only
thing it adds is unconditionally writing ALL the places at the end of
execution of BODY, which is of doubtful value. Why this `value-add' is
suspect is becauyse BODY can always use temporary variables to keep
temporary results.

I would prefer to use Kalle Olavi Niemitalo's SYMBOL-MACROLETF instead,
and update the places via regular SETQ/SETF/PSETF etc. No confusion,
nothing is hidden unerneath covers.

Others (jimka 2006 etc.) have used the names UPDATE/UPDATEF* for
functions which actually do Emacs-Lisp (cl.el)'s CALLF CALLF2.

On the whole I think Kaz Kylhelku's definitions for UPDATE/UPDATEF* have
"shady semantics" and limited use --- more like a problem in search of a
solution

--
Madhu


Madhu

unread,
Nov 7, 2009, 8:21:07 PM11/7/09
to

* Nicolas Neuss <87my30v...@ma-patru.mathematik.uni-karlsruhe.de> :
Wrote on Fri, 06 Nov 2009 09:45:56 +0100:

| Nicolas Neuss <last...@math.uni-karlsruhe.de> writes:

I don't think do you have a valid use case for UPDATE/UPDATEF*. The only


thing it adds is unconditionally writing ALL the places at the end of
execution of BODY, which is of doubtful value. Why this `value-add' is

suspect is because BODY can always use temporary variables to keep
temporary results.

I would prefer to use Kalle Olavi Niemitalo's SYMBOL-MACROLETF instead,
and update the places via regular SETQ/SETF/PSETF etc. No confusion,

nothing is hidden underneath covers.

Others (jimka 2006 etc.) have used the names UPDATE/UPDATEF* for
functions which actually do Emacs-Lisp (cl.el)'s CALLF CALLF2.

On the whole I think Kaz Kylhelku's definitions for UPDATE/UPDATEF* have

"shady semantics" and limited use --- more like a solution in search of a
problem

--
Madhu


budden

unread,
Nov 8, 2009, 5:23:58 AM11/8/09
to
Can we really always call setter code twice if we called retriever
code once?
If we can, I'd prefer symbol-macroletf. It gives us a freedom to call
updater
zero or more times.

If setf semantics supposes we can only call updater zero or one time,
I'd
prefer Kaz's update (with some othen name, of course).

Pascal J. Bourguignon

unread,
Nov 8, 2009, 5:57:22 AM11/8/09
to
budden <budde...@mail.ru> writes:

> Can we really always call setter code twice if we called retriever
> code once?

When it's the setter form returned by get-setf-expansion, yes. The
getter form returned by get-setf-expansion should be without side
effect, and the setter form returned by get-setf-expansion should have
only one side effect, that of modifying the place, AFAIK.


--
__Pascal Bourguignon__

Kaz Kylheku

unread,
Nov 8, 2009, 6:44:48 PM11/8/09
to

What if a getter and setter access a memory-mapped register?

Pascal J. Bourguignon

unread,
Nov 8, 2009, 6:48:29 PM11/8/09
to
Kaz Kylheku <kkyl...@gmail.com> writes:

This cannot count as a side effect, since virtual memory or cache
behavior preserves the state of the process, by definition (I don't
mean the state of the kernel about the process).

If you mean a time attack, then I could provide you a kernel that will
give you the same access time when the data is in cache or physical
memory as when it it is not.

--
__Pascal Bourguignon__

Kaz Kylheku

unread,
Nov 8, 2009, 7:10:25 PM11/8/09
to
On 2009-11-08, Pascal J. Bourguignon <p...@informatimago.com> wrote:
> Kaz Kylheku <kkyl...@gmail.com> writes:
>
>> On 2009-11-08, Pascal J. Bourguignon <p...@informatimago.com> wrote:
>>> budden <budde...@mail.ru> writes:
>>>
>>>> Can we really always call setter code twice if we called retriever
>>>> code once?
>>>
>>> When it's the setter form returned by get-setf-expansion, yes. The
>>> getter form returned by get-setf-expansion should be without side
>>> effect, and the setter form returned by get-setf-expansion should have
>>> only one side effect, that of modifying the place, AFAIK.
>>
>> What if a getter and setter access a memory-mapped register?
>
> This cannot count as a side effect, since virtual memory or cache
> behavior preserves the state of the process, by definition (I don't
> mean the state of the kernel about the process).

Since when would you access a memory mapped I/O register through cached
virtual memory?

*boggle*

Pascal J. Bourguignon

unread,
Nov 8, 2009, 7:42:17 PM11/8/09
to
Kaz Kylheku <kkyl...@gmail.com> writes:

Oh, you mean *I/O* register. You didn't write that first.

(Some processors with a lot of registers, or with register stacks, do
store some of their registers in memory.)


In any case, I never noted any provision for such a kind of place in
CLHS... I'd expect an implementation providing memory mapped I/O
register places to have a specific construct in the SYSTEM package or
some other implementation specific package.

--
__Pascal Bourguignon__

0 new messages