From: Spiros Bousbouras on
On 21 Nov, 12:10, p...(a)informatimago.com (Pascal J. Bourguignon)
wrote:
>
> Clearly, the expectation of naïve users such as me about a
> macroexpand-all function would be that it expands to the same special
> operators as macroexpand or macroexpand-1.

Whereas a more sophisticated user would read the documentation
instead of trying to figure out from just the name what it
does. I wonder what the expectations of a naive user would be with
regards to car or labels.

> I'd say that expanding to the special operators of the implementation
> is a specific need, and would need a more specific function, such as
> EXPAND-ALL-TO-IMPLEMENTATION-SPECIAL-OPERATORS.

And there is another "specific need" : macroexpand-all to things
defined in the standard but not below. So user macros would be
expanded but as soon as we reach standard macros then these would not
be expanded regardless of whether they are implemented as special
forms or not. This would be useful for debugging.

--
Who's your mama ?
From: Spiros Bousbouras on
On 18 Nov, 08:00, Pascal Costanza <p...(a)p-cos.net> wrote:
> Spiros Bousbouras wrote:
> > On 17 Nov, 20:15, Pascal Costanza <p...(a)p-cos.net> wrote:
> >> Spiros Bousbouras wrote:
>
> >>> Is there any established way for how such parallel
> >>> expansion/compilation is supposed to work ? Intuitively it seems to
> >>> me that in the scenario you're describing the implementation ought to
> >>> create one copy of a for the compilation and another for any
> >>> expansions which are not part of the compilation. If it doesn't do
> >>> that what makes you think that even your code will work correctly ?
> >>> There may be clashes with the use of new-cell-value.
> >> Huh? new-cell-value is lexically bound, so it's guaranteed to have its
> >> own binding per invocation.
>
> > Guaranteed by what ? This situation is not covered by the standard ,
> > is it ?
>
> Lexical bindings? Of course they are covered by the standard.

I was of course referring to the interaction between multithreading
and lexical bindings. *That* is not covered by the standard.

> > In any case in my code the variable a is also lexically bound.
>
> But you're performing side effects on this lexically bound variable,
> which may lead to race conditions when multiple threads expand this one
> macro. This is then a situation of concurrent write accesses to shared
> memory, which usually need to be synchronized. In a 'pure binding'
> (purely functional) situation, you don't have these problems.
>
> Yes, _multithreading_ is not covered by the standard,

Which brings us back to what I ask above: Is there any established
way for how such parallel expansion/compilation is supposed to work ?
To put it otherwise you're saying that my code violates some rules
but yours doesn't but you're not saying what these rules are , how
universal they are among implementations and where one could read
about those rules. And it's not clear what you mean by "side
effects". Are you saying that things like setf are completely
forbidden in the code which does the expansion ?

> but there are
> numerous multithreaded Common Lisp implementations, so if you want your
> code to be portable, you better take that into account.

No , if I want my code to be portable I only need to take into
account the standard and possible bugs/non-conformances in the
implementations. Portability *and* thread safety on the other hand ,
is a much more severe requirement.

Having said that it still seems likely to me that my code will work
everywhere yours will and that any multithreading Lisp implementation
worth its salt will create separate copies of the variable a for each
thread or synchronise access automatically.

--
Never attribute to conspiracy what can adequately be explained by
shared attitudes.
From: Duane Rettig on
On Nov 21, 3:24 am, Spiros Bousbouras <spi...(a)gmail.com> wrote:
> On 21 Nov, 10:13, p...(a)informatimago.com (Pascal J. Bourguignon)
> wrote:
>
>
>
>
>
> > Spiros Bousbouras <spi...(a)gmail.com> writes:
> > > I don't see why macroexpand-all cannot just return the dolist form
> > > itself if dolist is implemented as a special form.
>
> > Because  section "3.1.2.1.2.2 Macro Forms" says so.
>
> > DOLIST is specified to be a macro and as mentionned by section
> > "3.1.2.1.2.2 Macro Forms":
>
> >    An implementation is free to implement a Common Lisp special
> >    operator as a macro. An implementation is free to implement any
> >    macro operator as a special operator, but only if an equivalent
> >    definition of the macro is also provided.
>
> > it can be implemented as a special operator, however, in that case the
> > implementation must provide a macro definition expanding (eventually)
> > only to standard special operators, for the benefit of code walkers.
>
> But the standard does not mention macroexpand-all so it can behave
> any way it likes. Furthermore it's not unreasonable that it would not
> expand something which the implementation offers as a special form
> even if the standard says is a macro.

You'd have to figure out how to implement this in your macroexpand-all
function. Ultimately, this function will be implemented by calling
either macroexpand or macroexpand-1 at various times. The issue of
wether to macroexpand a special-form is a hard one, and one that
should be examined carefully at a lower level than the level you to
are discussing here - i.e. you are discussing "what" should happen,
when we need to deal with the "why" first wrt special operators.
Special operators are really black-boxes, but the concepts need to be
well-understood before it can be decided what to do with them.

> Or macroexpand-all could be
> designed to take an additional argument by which the programmer can
> switch between "expand everything the standard says is a macro" and
> "expand everything that this implementation actually (only)
> implements as a macro".
>
> But more to the point earlier in the thread Tobias said:
>
>     How does MACROEXPAND-ALL help the speed of compilation? Using
>     it, the implementation has to walk the syntax tree twice.
>
>     Do you know that it can in fact lead to an _impairment_ of
>     runtime speed because implementation which would previously
>     implement some macros as special forms cannot do so?
>
> Even now as I reread it it seems to be saying that just by making
> available macroexpand-all an implementation is prevented from
> implementing some macros as special forms. But I think what he meant
> was that if a piece of code uses macroexpand-all then when compiling
> this code an implementation can no longer use the special form
> version of a macro (if it has one) and has to use instead the actual
> macro expansion. So yes , I see how macroexpand-all can cause an
> impairment of *runtime* speed even when it is only called during
> compilation. But this could be solved by offering a switch as I
> mention above.

Yes, this comes close to an understanding of why special operators are
identified as such, and why the spec allows implementations to make
special operators out of macros.

The issue is this:

A function is a first-class object that can be identified as the
operator for a function call form. As an implementor, it is easy for
me to get my compiler back-end to grab a call to (foo 10) and make it
something else, performing the functionality of foo without actually
calling foo. This allows for a faster implementation of foo's
functionality.

However, a macro is not a stopping point for macroexpand, and so the
compiler back-end never sees the macro form - it only sees an
implementation of the macro in terms of lower-level (including
recursive) definitions - either function call forms or special forms.
But if a macroexpansion process were to always expand past special
forms, then your lowest level implementation would always and only be
function forms. This is not only inefficient, it is ludicrous. It is
why the spec doesn't require the standard special operators to also
have macro definitions (though Allegro CL does provide macro
definitions for all special operators).

So let's take a specific CASE (pun intended): Consider the following
definition:

(defun foo (x)
(declare (optimize speed (safety 0)))
(case x
((1 3 5 7 9) 'hi)
((2 4 6 8 27) 'bye)
(t 'nope)))

Note that a macroexpand of the case form normally turns into a cond:

CL-USER(1): (pprint (macroexpand '(case x
((1 3 5 7 9) 'hi)
((2 4 6 8 27) 'bye)
(t 'nope))))

(LET ()
(COND ((OR (EQL '1 X) (EQL '3 X) (EQL '5 X) (EQL '7 X) (EQL '9 X))
'HI)
((OR (EQL '2 X) (EQL '4 X) (EQL '6 X) (EQL '8 X) (EQL '27 X))
'BYE)
(T 'NOPE)))
CL-USER(2):

However, if we compile it (this one happens to be on x86-64) we get
something completely different than a cond statement:

CL-USER(2): (compile
(defun foo (x)
(declare (optimize speed (safety 0)))
(case x
((1 3 5 7 9) 'hi)
((2 4 6 8 27) 'bye)
(t 'nope))))
FOO
NIL
NIL
CL-USER(3): (disassemble 'foo)
;; disassembly of #<Function FOO>
;; formals: X
;; constant vector:
0: NOPE
1: HI
2: BYE

;; code start: #x1000994588:
0: 48 c1 cf 03 ror rdi,$3
4: 48 83 ff 1b cmp rdi,$27
8: 0f 87 86 00 00 jnbe 148
00
14: 48 8d 15 0b 00 leaq rdx,[rip+11] ; 32
00 00
21: 48 63 04 ba movslq rax,[rdx+rdi*4+0]
25: 48 03 c2 addq rax,rdx
28: ff e0 jmp *eax
30: 66 90 .align 4
32: 74 00 00 00 .long $116 ; 0: 148
36: 7f 00 00 00 .long $127 ; 1: 159
40: 86 00 00 00 .long $134 ; 2: 166
44: 7f 00 00 00 .long $127 ; 3: 159
48: 86 00 00 00 .long $134 ; 4: 166
52: 7f 00 00 00 .long $127 ; 5: 159
56: 86 00 00 00 .long $134 ; 6: 166
60: 7f 00 00 00 .long $127 ; 7: 159
64: 86 00 00 00 .long $134 ; 8: 166
68: 7f 00 00 00 .long $127 ; 9: 159
72: 74 00 00 00 .long $116 ; 10: 148
76: 74 00 00 00 .long $116 ; 11: 148
80: 74 00 00 00 .long $116 ; 12: 148
84: 74 00 00 00 .long $116 ; 13: 148
88: 74 00 00 00 .long $116 ; 14: 148
92: 74 00 00 00 .long $116 ; 15: 148
96: 74 00 00 00 .long $116 ; 16: 148
100: 74 00 00 00 .long $116 ; 17: 148
104: 74 00 00 00 .long $116 ; 18: 148
108: 74 00 00 00 .long $116 ; 19: 148
112: 74 00 00 00 .long $116 ; 20: 148
116: 74 00 00 00 .long $116 ; 21: 148
120: 74 00 00 00 .long $116 ; 22: 148
124: 74 00 00 00 .long $116 ; 23: 148
128: 74 00 00 00 .long $116 ; 24: 148
132: 74 00 00 00 .long $116 ; 25: 148
136: 74 00 00 00 .long $116 ; 26: 148
140: 86 00 00 00 .long $134 ; 27: 166
144: 00 00 00 00 .long $0
148: 49 8b 7e 36 movq rdi,[r14+54] ; NOPE
152: f8 clc
153: 4c 8b 74 24 10 movq r14,[rsp+16]
158: c3 ret
159: 49 8b 7e 3e movq rdi,[r14+62] ; HI
163: f8 clc
164: eb f3 jmp 153
166: 49 8b 7e 46 movq rdi,[r14+70] ; BYE
170: f8 clc
171: eb ec jmp 153
173: 90 nop
CL-USER(4):

The first question is: if case is a macro, and it expands into a cond
statement, how does the compiler get the macroexpansion to stop when
it sees a special-operator? And isn't this precisely the kind of
thing you're trying to do?

The other question (and it is not rhetorical) is: if you are able to
get macroexpansion to stop at a special operator, what will you do
with the form you get back? There might be a number of things you can
do with it, but since you have now a form that is no longer defined in
terms of lower-level CL operators, but instead in terms of a black box
that only the compiler knows how to implement, what will you do with
the form?

Duane

From: Pascal Costanza on
Spiros Bousbouras wrote:
>> Yes, _multithreading_ is not covered by the standard,
>
> Which brings us back to what I ask above: Is there any established
> way for how such parallel expansion/compilation is supposed to work ?
> To put it otherwise you're saying that my code violates some rules
> but yours doesn't but you're not saying what these rules are , how
> universal they are among implementations and where one could read
> about those rules. And it's not clear what you mean by "side
> effects". Are you saying that things like setf are completely
> forbidden in the code which does the expansion ?

No, I'm saying that side effects are known to be potential sources for
race conditions, while binding constructs are known to avoid race
conditions.

>> but there are
>> numerous multithreaded Common Lisp implementations, so if you want your
>> code to be portable, you better take that into account.
>
> No , if I want my code to be portable I only need to take into
> account the standard and possible bugs/non-conformances in the
> implementations. Portability *and* thread safety on the other hand ,
> is a much more severe requirement.
>
> Having said that it still seems likely to me that my code will work
> everywhere yours will and that any multithreading Lisp implementation
> worth its salt will create separate copies of the variable a for each
> thread or synchronise access automatically.

One of the two options is a bet, the other isn't. I know which of the
two I prefer. Good luck with your code, though.


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Spiros Bousbouras on
On Sat, 21 Nov 2009 09:42:45 -0800 (PST)
Duane Rettig <duane(a)franz.com> wrote:
> On Nov 21, 3:24 am, Spiros Bousbouras <spi...(a)gmail.com> wrote:
>
> > But the standard does not mention macroexpand-all so it can behave
> > any way it likes. Furthermore it's not unreasonable that it would not
> > expand something which the implementation offers as a special form
> > even if the standard says is a macro.
>
> You'd have to figure out how to implement this in your macroexpand-all
> function.

I'm not writing a macroexpand-all function. The possibility occurred to
me and I considered starting a thread on the subject (and I may still
do) but I imagine it cannot be done portably. I was simply saying that
it would be convenient if the standard included a macroexpand-all
function so that every conforming implementation would have it.

> Ultimately, this function will be implemented by calling
> either macroexpand or macroexpand-1 at various times.

If one wants to do it portably then perhaps it's unavoidable that
macroexpand or macroexpand-1 would have to be used. But if the
implementation does it then it doesn't have such a restriction.

> The issue of
> wether to macroexpand a special-form is a hard one, and one that
> should be examined carefully at a lower level than the level you to
> are discussing here - i.e. you are discussing "what" should happen,
> when we need to deal with the "why" first wrt special operators.

In programming one usually starts with what one wants to achieve. I
have explained during the course of the thread why I would want
macroexpand-all but I suspect you haven't read the whole thread.

In my opening post I gave a specific piece of code but I used the name
minimal-compile instead of macroexpand-all because at the time I hadn't
come across the name macroexpand-all. I was informed in this thread
that macroexpand-all is an established (albeit non-standard) name for
the functionality I want and I also came across the information that
SBCL offers macroexpand-all so here's a working piece of code:

; Working but SBCL specific because of sb-cltl2:macroexpand-all and
; (require "sb-cltl2")
(require "sb-cltl2")
(let ((a 0))
(defmacro memory (&rest body)
(prog2
(incf a)
(sb-cltl2:macroexpand-all
`(symbol-macrolet ((cell ,a))
,@body))
(decf a))))

If macroexpand-all expands recursively user defined macros and the
arguments given to macros defined by the implementation but does not
expand the macros provided by the implementation then the above will
still work. So for example if I do

(macroexpand-all
'(memory ...
(when test-form form)))

and I get

(<expansion of memory>
(when <expansion of test-form> <expansion of form>))

instead of

(<expansion of memory>
(<expansion of when> <expansion of test-form> <expansion of form>))


it is still adequate for my purposes. That's why I didn't have to
concern myself with the issue of which standard macros the
implementation implements as special forms.

[...]

> However, a macro is not a stopping point for macroexpand, and so the
> compiler back-end never sees the macro form - it only sees an
> implementation of the macro in terms of lower-level (including
> recursive) definitions - either function call forms or special forms.
> But if a macroexpansion process were to always expand past special
> forms, then your lowest level implementation would always and only be
> function forms. This is not only inefficient, it is ludicrous.

Beyond ludicrous isn't it actually unworkable ? Function forms
evaluate all their arguments but some times you don't want all the
arguments evaluated.

> It is
> why the spec doesn't require the standard special operators to also
> have macro definitions (though Allegro CL does provide macro
> definitions for all special operators).

You mean that if I do

(some-allegro-specific-package:macroexpand-all
'(when test-form form))

I will get something which only has function calls ? I'd love to know
how you manage this.

[...]

> The first question is: if case is a macro, and it expands into a cond
> statement, how does the compiler get the macroexpansion to stop when
> it sees a special-operator? And isn't this precisely the kind of
> thing you're trying to do?

As I've said I'm not actually writing a macroexpand-all function but it
seems to me that this particular issue can be easily solved portably:

(defun macroexpand-all (form)
(if (and (consp form)
(special-operator-p (first form)))
; It's a special operator so it doesn't need to be expanded
...))

> The other question (and it is not rhetorical) is: if you are able to
> get macroexpansion to stop at a special operator, what will you do
> with the form you get back? There might be a number of things you can
> do with it, but since you have now a form that is no longer defined in
> terms of lower-level CL operators, but instead in terms of a black box
> that only the compiler knows how to implement, what will you do with
> the form?

I can think of at least 2 possibilities:

1) Pass the form to the compiler for further compilation. See the example
above.

2) Inspect the form visually for debugging purposes. So let's say for
example that my code defines macros mac1 and mac2 and that the
expansion of mac2 is influenced by whether it is called within the
scope of a mac1 call. In order to verify that the interaction between
mac1 and mac2 works correctly I may want to do something like

(macroexpand-all
'(mac1 (defun name (args) (mac2))))

and verify that I get the kind of expansion that I want. For this it
does not help me in any way if defun itself gets expanded because I
only want to verify that mac1 and mac2 expand to the right thing. On
the contrary the result will probably be clearer if defun does not get
expanded than if it gets expanded to some implementation specific
symbols which would be unfamiliar to me.

So , come to think of it , a macroexpand-all intended as a debugging
tool would be more useful if it accepted a key argument called :test.
This would be a function which accepts 1 argument which should be a
symbol and returns true or false. macroexpand-all would only try to
expand symbols for which the test function returns true (or all the
symbols if no test function was given). So for the example above you
would do

(macroexpand-all
'(mac1 (defun name (args) (mac2)))
:test #'(lambda (item)
(find item '(mac1 mac2))))

--
THE COURT: I forbid her from singing during the trial. I will not
permit singing in this Courtroom.
http://www.law.umkc.edu/faculty/projects/ftrials/Chicago7/Collins.html

First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4 5 6 7 8
Prev: Lisp sucks!
Next: grabbing key presses