I want to write a macro that wraps defclass which evaluates (and
type-checks) initforms at macroexpand time. I can do this as follows:
(defmacro my-defclass (...)
`(let ((initvals (list ,@(extract-iniforms ...))))
(type-check initvals)
(eval `(defclass ... ,@(stick-initvals-in-specs ... initvals)))))
But of course this triggers the general rule that if you're using eval
you're almost certainly doing something wrong. I think eval is actually
necessary here because you need to produce a macroexpanded form to
evaluate the initforms in the proper lexical environment, and then you
have to use those values to produce *another* form which is the final
macroexpansion, and this two-phase process requires you to explicitly
eval either the initforms or the final expression (with lexical scoping
requirements indicating the latter). But I thought I'd check with the
experts before I totally threw in the towel.
Thanks,
rg
Ron Garret <·········@flownet.com> writes:
> I want to write a macro that wraps defclass which evaluates (and
> type-checks) initforms at macroexpand time.
I'm too tired right now, so I don't quite get why you want to do this,
but couldn't you just use (assuming you accept a litte AMOP)
ensure-class?
--
(espen)
Ron Garret <·········@flownet.com> writes:
> I want to write a macro that wraps defclass which evaluates (and
> type-checks) initforms at macroexpand time. I can do this as follows:
>
> (defmacro my-defclass (...)
> `(let ((initvals (list ,@(extract-iniforms ...))))
> (type-check initvals)
> (eval `(defclass ... ,@(stick-initvals-in-specs ... initvals)))))
If you want to typecheck at macroexpansion time, don't do it at run time!
(defmacro my-defclass (...)
(let ((initvals (extract-iniforms ...))) ; MEX time
(type-check initvals) ; MEX time
`(defclass ... ,@(stick-initvals-in-specs ... initvals)))) ; RUN time
should do.
--
__Pascal Bourguignon__ http://www.informatimago.com/
WARNING: This product warps space and time in its vicinity.
On Feb 15, 3:00 pm, Pascal Bourguignon <····@informatimago.com> wrote:
> Ron Garret <·········@flownet.com> writes:
> > I want to write a macro that wraps defclass which evaluates (and
> > type-checks) initforms at macroexpand time. I can do this as follows:
>
> > (defmacro my-defclass (...)
> > `(let ((initvals (list ,@(extract-iniforms ...))))
> > (type-check initvals)
> > (eval `(defclass ... ,@(stick-initvals-in-specs ... initvals)))))
>
> If you want to typecheck at macroexpansion time, don't do it at run time!
>
> (defmacro my-defclass (...)
> (let ((initvals (extract-iniforms ...))) ; MEX time
> (type-check initvals) ; MEX time
> `(defclass ... ,@(stick-initvals-in-specs ... initvals)))) ; RUN time
>
> should do.
ROFL.
It's times like these that one regrets having been sucked in by
flamebait like ``Lisp-2 is a hacky but workable solution to the
problem of unintended name capture in macros.''
Ron Garret wrote:
> I want to write a macro that wraps defclass which evaluates (and
> type-checks) initforms at macroexpand time. I can do this as follows:
>
> (defmacro my-defclass (...)
> `(let ((initvals (list ,@(extract-iniforms ...))))
> (type-check initvals)
> (eval `(defclass ... ,@(stick-initvals-in-specs ... initvals)))))
>
> But of course this triggers the general rule that if you're using eval
> you're almost certainly doing something wrong. I think eval is actually
> necessary here because you need to produce a macroexpanded form to
> evaluate the initforms in the proper lexical environment, and then you
> have to use those values to produce *another* form which is the final
> macroexpansion, and this two-phase process requires you to explicitly
> eval either the initforms or the final expression (with lexical scoping
> requirements indicating the latter). But I thought I'd check with the
> experts before I totally threw in the towel.
No, an eval should not be necessary here because the initforms in a
defclass are evaluated, and they are even evaluated in the current
lexical environment.
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/
In article <···············@mid.individual.net>,
Pascal Costanza <··@p-cos.net> wrote:
> Ron Garret wrote:
> > I want to write a macro that wraps defclass which evaluates (and
> > type-checks) initforms at macroexpand time. I can do this as follows:
> >
> > (defmacro my-defclass (...)
> > `(let ((initvals (list ,@(extract-iniforms ...))))
> > (type-check initvals)
> > (eval `(defclass ... ,@(stick-initvals-in-specs ... initvals)))))
> >
> > But of course this triggers the general rule that if you're using eval
> > you're almost certainly doing something wrong. I think eval is actually
> > necessary here because you need to produce a macroexpanded form to
> > evaluate the initforms in the proper lexical environment, and then you
> > have to use those values to produce *another* form which is the final
> > macroexpansion, and this two-phase process requires you to explicitly
> > eval either the initforms or the final expression (with lexical scoping
> > requirements indicating the latter). But I thought I'd check with the
> > experts before I totally threw in the towel.
>
> No, an eval should not be necessary here because the initforms in a
> defclass are evaluated, and they are even evaluated in the current
> lexical environment.
I was unclear about one of my design goals. I want the initforms to be
evaluated only once, preferably at macroexpand time. I also want them
to be properly lexically scoped. So for example:
(let ((x 1)) (defclass foo () ((x :initform (print (incf x))))))
This would print a different number every time an instance of FOO is
created.
If you replaced defclass with my-defclass it should print one number (2)
when the my-defclass form is evaluated, not print anything when
instances of FOO are created, and all instances of FOO should have the X
slot initialized to 2. (And if the initform evaluated to something of
the wrong type that error should be signaled at macroexpand time.)
And yes, I know that my code doesn't do all that. But it comes closer
than anything else I've been able to manage.
rg
In article <·······························@news.gha.chartermi.net>,
Ron Garret <·········@flownet.com> wrote:
> I was unclear about one of my design goals. I want the initforms to be
> evaluated only once, preferably at macroexpand time. I also want them
> to be properly lexically scoped. So for example:
>
> (let ((x 1)) (defclass foo () ((x :initform (print (incf x))))))
>
> This would print a different number every time an instance of FOO is
> created.
>
>
> If you replaced defclass with my-defclass it should print one number (2)
> when the my-defclass form is evaluated, not print anything when
> instances of FOO are created, and all instances of FOO should have the X
> slot initialized to 2. (And if the initform evaluated to something of
> the wrong type that error should be signaled at macroexpand time.)
What you want to do is expand MY-DEFCLASS into DEFCLASS, where all the
initforms are replaced with something like
(let ((val <initform>))
(check-type val <type>)
val)
E.g. the above form (using MY-DEFCLASS in place of DEFCLASS) would
expand into:
(let ((x 1))
(defclass foo ()
((x :initform (let ((val (print (incf x))))
(check-type val 'number)
val)))))
--
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
Ron Garret wrote:
> I was unclear about one of my design goals. I want the initforms to be
> evaluated only once, preferably at macroexpand time. I also want them
> to be properly lexically scoped. So for example:
>
> (let ((x 1)) (defclass foo () ((x :initform (print (incf x))))))
>
> This would print a different number every time an instance of FOO is
> created.
>
>
> If you replaced defclass with my-defclass it should print one number (2)
> when the my-defclass form is evaluated, not print anything when
> instances of FOO are created, and all instances of FOO should have the X
> slot initialized to 2. (And if the initform evaluated to something of
> the wrong type that error should be signaled at macroexpand time.)
>
> And yes, I know that my code doesn't do all that. But it comes closer
> than anything else I've been able to manage.
In other words, you want this?
(let ((x 1))
(let ((g123 (print (incf x))))
(defclass foo () ((x :initform g123)))))
Then that's what your macro should expand into... ;)
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/
In article <···············@mid.individual.net>,
Pascal Costanza <··@p-cos.net> wrote:
> Ron Garret wrote:
>
> > I was unclear about one of my design goals. I want the initforms to be
> > evaluated only once, preferably at macroexpand time. I also want them
> > to be properly lexically scoped. So for example:
> >
> > (let ((x 1)) (defclass foo () ((x :initform (print (incf x))))))
> >
> > This would print a different number every time an instance of FOO is
> > created.
> >
> >
> > If you replaced defclass with my-defclass it should print one number (2)
> > when the my-defclass form is evaluated, not print anything when
> > instances of FOO are created, and all instances of FOO should have the X
> > slot initialized to 2. (And if the initform evaluated to something of
> > the wrong type that error should be signaled at macroexpand time.)
> >
> > And yes, I know that my code doesn't do all that. But it comes closer
> > than anything else I've been able to manage.
>
> In other words, you want this?
>
> (let ((x 1))
> (let ((g123 (print (incf x))))
> (defclass foo () ((x :initform g123)))))
>
>
> Then that's what your macro should expand into... ;)
Duh. I was trying to capture the values and stick those into the
defclass form, but of course lexical variables capturing those values
work just as well. (I guess I forgot that the iniforms are properly
lexically scoped.)
Now, where's my dunce cap?
Thanks.
rg
Pascal Costanza <··@p-cos.net> writes:
> Ron Garret wrote:
>> I want to write a macro that wraps defclass which evaluates (and
>> type-checks) initforms at macroexpand time. I can do this as
>> follows:
>>
>> (defmacro my-defclass (...)
>> `(let ((initvals (list ,@(extract-iniforms ...))))
>> (type-check initvals)
>> (eval `(defclass ... ,@(stick-initvals-in-specs ... initvals)))))
>>
>> But of course this triggers the general rule that if you're using
>> eval you're almost certainly doing something wrong. I think eval is
>> actually necessary here because you need to produce a macroexpanded
>> form to evaluate the initforms in the proper lexical environment,
>> and then you have to use those values to produce *another* form
>> which is the final macroexpansion, and this two-phase process
>> requires you to explicitly eval either the initforms or the final
>> expression (with lexical scoping requirements indicating the
>> latter). But I thought I'd check with the experts before I totally
>> threw in the towel.
>
> No, an eval should not be necessary here because the initforms in a
> defclass are evaluated, and they are even evaluated in the current
> lexical environment.
Well, this is another problem: can you do the type check at macro
expansion time at all if you don't have access to the lexical
environment where defclass runs?
(labels ((proper-list-p (x) ...))
(my-defclass c ()
((a :type '(satisfies proper-list-p) :initform '()))))
--
__Pascal Bourguignon__ http://www.informatimago.com/
"This machine is a piece of GAGH! I need dual Opteron 850
processors if I am to do battle with this code!"
In article <··············@thalassa.informatimago.com>,
Pascal Bourguignon <···@informatimago.com> wrote:
> Pascal Costanza <··@p-cos.net> writes:
> > Ron Garret wrote:
> >> I want to write a macro that wraps defclass which evaluates (and
> >> type-checks) initforms at macroexpand time. I can do this as
> >> follows:
> >>
> >> (defmacro my-defclass (...)
> >> `(let ((initvals (list ,@(extract-iniforms ...))))
> >> (type-check initvals)
> >> (eval `(defclass ... ,@(stick-initvals-in-specs ... initvals)))))
> >>
> >> But of course this triggers the general rule that if you're using
> >> eval you're almost certainly doing something wrong. I think eval is
> >> actually necessary here because you need to produce a macroexpanded
> >> form to evaluate the initforms in the proper lexical environment,
> >> and then you have to use those values to produce *another* form
> >> which is the final macroexpansion, and this two-phase process
> >> requires you to explicitly eval either the initforms or the final
> >> expression (with lexical scoping requirements indicating the
> >> latter). But I thought I'd check with the experts before I totally
> >> threw in the towel.
> >
> > No, an eval should not be necessary here because the initforms in a
> > defclass are evaluated, and they are even evaluated in the current
> > lexical environment.
>
> Well, this is another problem: can you do the type check at macro
> expansion time at all if you don't have access to the lexical
> environment where defclass runs?
>
> (labels ((proper-list-p (x) ...))
> (my-defclass c ()
> ((a :type '(satisfies proper-list-p) :initform '()))))
I think I've realized one source of confusion: there are two different
macroexpansion times and three different run times involved here:
1. Macroexpansion of my-defclass
2. Running my-defclass
3. Macroexpansion of the defclass that my-defclass expands into
4. Running that defclass and
5. Creating instances of the resulting class
What I really want is for the initform evaluation and type checking to
happen sometime before step 5. Exactly where doesn't really matter that
much to me (so it probably makes the most sense to do it in step 2).
rg