From: Tamas K Papp on
Hi everyone,

I wanted to define some syntactic sugar for array element access in an
application. Fortunately CL is a Lisp2, so I can do something like

(macrolet ((a (i)
`(aref a ,i)))
...)

but when I have this inside an ITER (an inner loop within an outer
loop), ITERATE complains that it cannot walk the MACROLET. After
reading the HS, I found a neat solution:

(flet ((a (i)
(aref a i))
((setf a) (value i)
(setf (aref a i) value)))
...)

which works really nice. SBCL inlines the whole thing without further
declarations. CL is indeed amazing. I can't think of any other
language that would let me do this.

I just have two questions:

1. if inside the body ... I have eg

(incf (a (convoluted-index-calculation j)))

is CONVOLUTED-INDEX-CALCULATION guaranteed to be called only once?
When I macroexpand this form, I get

(LET* ((#:TMP955 (CONVOLUTED-INDEX-CALCULATION J))
(#:G956 1)
(#:NEW954 (+ (A #:TMP955) #:G956)))
(FUNCALL #'(SETF A) #:NEW954 #:TMP955))

but is single evaluation guaranteed by the standard? Explanations and
pointers to the relevant section in the HS would be appreciated (I
know it is within 5.1, but could not find it).

2. If the elements of A (which is a vector) represent a matrix, arranged in
a column-major order, I would use

(flet ((a (i j)
(aref a (+ (* j nrow) i)))
((setf a) (value i j)
(setf (aref a (+ (* j nrow) i)) value)))
...)

but then it is very likely that the (+ (* j nrow) i) calculation would
happen twice. How can I avoid this without using local macros (which
interfere with iterate and code walkers)? AFAIK SETF expanders can only
be defined globally.

Thanks,

Tamas

From: Lieven Marchand on
Tamas K Papp <tkpapp(a)gmail.com> writes:

> I just have two questions:
>
> 1. if inside the body ... I have eg
>
> (incf (a (convoluted-index-calculation j)))
>
> is CONVOLUTED-INDEX-CALCULATION guaranteed to be called only once?
> When I macroexpand this form, I get
>
> (LET* ((#:TMP955 (CONVOLUTED-INDEX-CALCULATION J))
> (#:G956 1)
> (#:NEW954 (+ (A #:TMP955) #:G956)))
> (FUNCALL #'(SETF A) #:NEW954 #:TMP955))
>
> but is single evaluation guaranteed by the standard? Explanations and
> pointers to the relevant section in the HS would be appreciated (I
> know it is within 5.1, but could not find it).

5.1.1.1 2. For the macros that manipulate places (push, pushnew, remf,
incf, decf, shiftf, rotatef, psetf, setf, pop, and those defined by
define-modify-macro) the subforms of the macro call are evaluated
exactly once in left-to-right order, with the subforms of the places
evaluated in the order specified in (1).
From: Pascal J. Bourguignon on
Tamas K Papp <tkpapp(a)gmail.com> writes:

> Hi everyone,
>
> I wanted to define some syntactic sugar for array element access in an
> application. Fortunately CL is a Lisp2, so I can do something like
>
> (macrolet ((a (i)
> `(aref a ,i)))
> ...)
>
> but when I have this inside an ITER (an inner loop within an outer
> loop), ITERATE complains that it cannot walk the MACROLET. After
> reading the HS, I found a neat solution:
>
> (flet ((a (i)
> (aref a i))
> ((setf a) (value i)
> (setf (aref a i) value)))
> ...)
>
> which works really nice. SBCL inlines the whole thing without further
> declarations. CL is indeed amazing. I can't think of any other
> language that would let me do this.
>
> I just have two questions:
>
> 1. if inside the body ... I have eg
>
> (incf (a (convoluted-index-calculation j)))
>
> is CONVOLUTED-INDEX-CALCULATION guaranteed to be called only once?
> When I macroexpand this form, I get
>
> (LET* ((#:TMP955 (CONVOLUTED-INDEX-CALCULATION J))
> (#:G956 1)
> (#:NEW954 (+ (A #:TMP955) #:G956)))
> (FUNCALL #'(SETF A) #:NEW954 #:TMP955))
>
> but is single evaluation guaranteed by the standard? Explanations and
> pointers to the relevant section in the HS would be appreciated (I
> know it is within 5.1, but could not find it).

Yes, it is specified that it should be evaluated only once.

5.1.1.1 says:

2. For the macros that manipulate places (push, pushnew, remf,
incf, decf, shiftf, rotatef, psetf, setf, pop, and those defined
by define-modify-macro) the subforms of the macro call are
evaluated exactly once in left-to-right order, with the subforms
of the places evaluated in the order specified in (1).

> 2. If the elements of A (which is a vector) represent a matrix, arranged in
> a column-major order, I would use
>
> (flet ((a (i j)
> (aref a (+ (* j nrow) i)))
> ((setf a) (value i j)
> (setf (aref a (+ (* j nrow) i)) value)))
> ...)
>
> but then it is very likely that the (+ (* j nrow) i) calculation would
> happen twice.
> AFAIK SETF expanders can only be defined globally.

Indeed.


> How can I avoid this without using local macros (which
> interfere with iterate and code walkers)?

(flet ((row-major-a (rm) (aref a rm))
((setf row-major-a) (value rm) (setf (aref a rm) value))
(a-row-major-index (i j) (+ (* j nrow) i)))
(incf (row-major-a (a-row-major-index i j))))


--
__Pascal Bourguignon__
From: Frode V. Fjeld on

> Tamas K Papp <tkpapp(a)gmail.com> writes:
>
>> but is single evaluation guaranteed by the standard?

Lieven Marchand <mal(a)wyrd.be> writes:

> 5.1.1.1 2. For the macros that manipulate places (push, pushnew, remf,
> incf, decf, shiftf, rotatef, psetf, setf, pop, and those defined by
> define-modify-macro) the subforms of the macro call are evaluated
> exactly once in left-to-right order, with the subforms of the places
> evaluated in the order specified in (1).

I believe also that much of the setf machinery is there to allow people
to write setters that preserves left-to-right, single evaluation.

I think that this aspect of CL is a great achievement in composability.

--
Frode V. Fjeld