From: Michal Krupka
Subject: funcallable-standard-class question
Date:
Message-ID: <emtqe5$il3$1@aioe.org>
In my little experimental project, I need to define a class with
funcallable-standard-class as a metaclass and call the function
set-funcallable-instance-function on its instances. I want to make it
work in all main CL implementations.
SBCL and LispWorks seems to work OK, prompt.franz.com generates error
"Class #<STANDARD-CLASS STANDARD-OBJECT> is not yet finalized." when
evaluating class definition:
(defclass test ()
()
(:metaclass funcallable-standard-class))
I have no access to other CL implementations.
In my package definition I use the following to import MOP symbols:
(:use "COMMON-LISP"
#+allegro "MOP"
#+clisp "CLOS"
#+lispworks "CLOS"
#+openmcl "CCL"
#+mcl "CCL"
#+cmu "CLOS-MOP"
#+sbcl "SB-MOP")
So the questions are: How make it work in Allegro? Will it work in
other main implementations?
I am aware of Closer to MOP project but I don't want to use it for the
present since I want to keep my project as simple as possible (it has
only several hundreds of lines in a single file).
Thanks,
Michal
Michal Krupka wrote:
> In my little experimental project, I need to define a class with
> funcallable-standard-class as a metaclass and call the function
> set-funcallable-instance-function on its instances. I want to make it
> work in all main CL implementations.
>
> SBCL and LispWorks seems to work OK, prompt.franz.com generates error
> "Class #<STANDARD-CLASS STANDARD-OBJECT> is not yet finalized." when
> evaluating class definition:
>
> (defclass test ()
> ()
> (:metaclass funcallable-standard-class))
>
> I have no access to other CL implementations.
That's a weird error. The CLOS MOP states that a class may be finalized
as late as the first instance of a class is created. Other uses of a
class that requires its finalization may simply signal an error. I fail
to see how your example should be covered by these restrictions, though.
Furthermore, there seems to be a bug in that Allegro seems to select
standard-object as the default superclass for a
funcallable-standard-class instead of the funcallable-standard-object as
specified.
The workarounds are this:
(unless (class-finalized-p (find-class 'standard-object))
(finalize-inheritance (find-class 'standard-object)))
(defclass ...)
and
(unless (class-finalized-p (find-class 'funcallable-standard-object))
(finalize-inheritance (find-class 'funcallable-standard-object)))
(defclass test (funcallable-standard-object)
()
(:metaclass funcallable-standard-class))
> In my package definition I use the following to import MOP symbols:
>
> (:use "COMMON-LISP"
> #+allegro "MOP"
> #+clisp "CLOS"
> #+lispworks "CLOS"
> #+openmcl "CCL"
> #+mcl "CCL"
> #+cmu "CLOS-MOP"
> #+sbcl "SB-MOP")
>
> So the questions are: How make it work in Allegro?
See above.
> Will it work in other main implementations?
I think it should.
> I am aware of Closer to MOP project but I don't want to use it for the
> present since I want to keep my project as simple as possible (it has
> only several hundreds of lines in a single file).
This case is actually not covered in Closer to MOP, so thanks. ;)
However, the usual meta question applies: What do you actually want to
achieve? Maybe there is an easier way to achieve what you want without
using the CLOS MOP...
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: Michal Krupka
Subject: Re: funcallable-standard-class question
Date:
Message-ID: <emu177$2ln$1@aioe.org>
Thank you, Pascal.
On 2006-12-27 14:41:58 +0100, Pascal Costanza <··@p-cos.net> said:
> This case is actually not covered in Closer to MOP, so thanks. ;)
>
> However, the usual meta question applies: What do you actually want to
> achieve? Maybe there is an easier way to achieve what you want without
> using the CLOS MOP...
I am working on an interpreter of an experimental single-purpose
language. The interpreter works by transforming source code to a number
of closures and running these closures. In addition, users can embed
ordinary Lisp code into the source code. In both cases, my interpreter
needs to know the count of required parameters of all functions.
To obtain the required parameter count of ordinary Lisp functions I
first try to call function-lambda-expression (or function-lambda-list
in Lispworks) and analyze the lambda list. If
function-lambda-expression returns nil, I must ask the user to enter
the required data manually and store them in a hashtable.
In the case of functions generated by the interpreter I am able to
remember the required parameter count myself in the time of their
creation. Now there seem to be two natural possibilities where to store
these data: within these functions themselves or in a hashtable.
I prefer to use the former and define my functions as funcallable
objects with an additional slot as the required storage. The letter
solution would lead to a weak hashtable of some kind (there can be a
great number of anonymous functions generated by the interpreter), and
weak hashtables seem to be much less standardized than the MOP.
Michal
>
>
> Pascal
Michal Krupka wrote:
> Thank you, Pascal.
>
> On 2006-12-27 14:41:58 +0100, Pascal Costanza <··@p-cos.net> said:
>> This case is actually not covered in Closer to MOP, so thanks. ;)
>>
>> However, the usual meta question applies: What do you actually want to
>> achieve? Maybe there is an easier way to achieve what you want without
>> using the CLOS MOP...
>
> I am working on an interpreter of an experimental single-purpose
> language. The interpreter works by transforming source code to a number
> of closures and running these closures. In addition, users can embed
> ordinary Lisp code into the source code. In both cases, my interpreter
> needs to know the count of required parameters of all functions.
>
> To obtain the required parameter count of ordinary Lisp functions I
> first try to call function-lambda-expression (or function-lambda-list in
> Lispworks) and analyze the lambda list. If function-lambda-expression
> returns nil, I must ask the user to enter the required data manually and
> store them in a hashtable.
>
> In the case of functions generated by the interpreter I am able to
> remember the required parameter count myself in the time of their
> creation. Now there seem to be two natural possibilities where to store
> these data: within these functions themselves or in a hashtable.
>
> I prefer to use the former and define my functions as funcallable
> objects with an additional slot as the required storage. The letter
> solution would lead to a weak hashtable of some kind (there can be a
> great number of anonymous functions generated by the interpreter), and
> weak hashtables seem to be much less standardized than the MOP.
OK, this is indeed a good use of the funcallable-standard-object
machinery in the CLOS MOP.
However, a straightforward way to get a similar effect is to store the
metadata about a function in a struct:
(defstruct myfunction args fun)
Whenever you have to call function, you would then have to say something
like (funcall (myfunction-fun ...) ...), but it should be possible to
hide this away with some macrology.
Another way is to go back to plain old symbols:
(let ((myfunction (make-symbol)))
(setf (get myfunction 'args) 5)
(setf (symbol-function myfunction) (lambda (a b c d e) ...))
myfunction)
Then you can simply call them via (funcall myfunction ...), since
funcall already recognizes symbols as potential functions. If you
organize your code appropriately, it should even be possible to use
these generated symbols directly, like in (myfunction ...).
I hope this helps.
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/
Michal Krupka wrote:
> In my little experimental project, I need to define a class with
> funcallable-standard-class as a metaclass and call the function
> set-funcallable-instance-function on its instances. I want to make it
> work in all main CL implementations.
>
> SBCL and LispWorks seems to work OK, prompt.franz.com generates error
> "Class #<STANDARD-CLASS STANDARD-OBJECT> is not yet finalized." when
> evaluating class definition:
>
> (defclass test ()
> ()
> (:metaclass funcallable-standard-class))
Try one of these
(defclass test (standard-generic-function)
()
(:metaclass clos:funcallable-standard-class))
(defclass test (generic-function)
()
(:metaclass clos:funcallable-standard-class))
depending on exactly what you are trying to do.
I don't know what the "other implementions" are doing, and whether you
can actually construct a callable generic function without specifying
the superclasses of your test class, but according to the normal clos
definitions objects or your original class would be standard-objects,
not funcallable-standard-objects, much less generic-functions. A
standard-object doesn't support being funcalled, etc.
The error you received was cryptic, but is being signaled by
mop:validate-superclass. Some history: The X3J13 subcommittee that
drafted the COS standard recommended back in 1989 that X3J13 approve the
CLOS standard as an addition to the language, but not approve the MOP
specification since there was no experience using it and there was
considerable doubt that all the details were correct and consistent.
In fact the MOP spec has proven pretty good. but there are a number of
non-obvious brainos that aren't so very important since they don't
affect normal code, or even code very distant from being normal code.
One of these braino areas surrounded validate-superclass. There were
some email discussions with Gregor and others back then that suggested
some improvements to the tests performed by validate-superclass. ACL
tries to implement them (although they are certainly not yet entirely
correct -- it's an area that is hard to think about!) There is a
residual question here, whether specifying a metaclass of
funcallable-standard-class should modify the specified superclass list
as required by the implementation, but I've survived this long without
thinking hard about this and hope ti live out the rest of my days wthout
having to do so.
Steven Haflich wrote:
> Michal Krupka wrote:
>> In my little experimental project, I need to define a class with
>> funcallable-standard-class as a metaclass and call the function
>> set-funcallable-instance-function on its instances. I want to make it
>> work in all main CL implementations.
>>
>> SBCL and LispWorks seems to work OK, prompt.franz.com generates error
>> "Class #<STANDARD-CLASS STANDARD-OBJECT> is not yet finalized." when
>> evaluating class definition:
>>
>> (defclass test ()
>> ()
>> (:metaclass funcallable-standard-class))
>
> Try one of these
>
> (defclass test (standard-generic-function)
> ()
> (:metaclass clos:funcallable-standard-class))
>
> (defclass test (generic-function)
> ()
> (:metaclass clos:funcallable-standard-class))
>
> depending on exactly what you are trying to do.
>
> I don't know what the "other implementions" are doing, and whether you
> can actually construct a callable generic function without specifying
> the superclasses of your test class, but according to the normal clos
> definitions objects or your original class would be standard-objects,
> not funcallable-standard-objects, much less generic-functions. A
> standard-object doesn't support being funcalled, etc.
The CLOS MOP specification is refreshingly unambiguous about this case.
In "Inheritance Structure of Metaobject Classes", it states the following:
"The class standard-object is the default direct superclass of the class
standard-class. When an instance of the class standard-class is created,
and no direct superclasses are explicitly specified, it defaults to the
class standard-object. In this way, any behavior associated with the
class standard-object will be inherited, directly or indirectly, by all
instances of the class standard-class. A subclass of standard-class may
have a different class as its default direct superclass, but that class
must be a subclass of the class standard-object.
The same is true for funcallable-standard-class and
funcallable-standard-object."
Due to this thread, I have added a test case to MOP Feature Tests (part
of the Closer project), and it's indeed the case that of the tested CL
implementations, only Allegro seems to deviate here.
> The error you received was cryptic, but is being signaled by
> mop:validate-superclass. Some history: The X3J13 subcommittee that
> drafted the COS standard recommended back in 1989 that X3J13 approve the
> CLOS standard as an addition to the language, but not approve the MOP
> specification since there was no experience using it and there was
> considerable doubt that all the details were correct and consistent. In
> fact the MOP spec has proven pretty good. but there are a number of
> non-obvious brainos that aren't so very important since they don't
> affect normal code, or even code very distant from being normal code.
> One of these braino areas surrounded validate-superclass. There were
> some email discussions with Gregor and others back then that suggested
> some improvements to the tests performed by validate-superclass. ACL
> tries to implement them (although they are certainly not yet entirely
> correct -- it's an area that is hard to think about!) There is a
> residual question here, whether specifying a metaclass of
> funcallable-standard-class should modify the specified superclass list
> as required by the implementation, but I've survived this long without
> thinking hard about this and hope ti live out the rest of my days wthout
> having to do so.
Superclass validation in the CLOS MOP is completely useless. A language
in which most corner cases lead to unspecified or even undefined
behavior suddenly performs spurious compatibility tests and even makes
it hard to correctly specialize the involved generic function
(validate-superclass). That's weird, to say the least.
My suggestion for a next revision of the CLOS MOP (if this ever happens)
would be to drop this.
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/