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

From: Pascal Costanza
Subject: Re: funcallable-standard-class question
Date: 
Message-ID: <4vfbhaF1b5c00U1@mid.individual.net>
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
From: Pascal Costanza
Subject: Re: funcallable-standard-class question
Date: 
Message-ID: <4vfgfaF1c99i7U1@mid.individual.net>
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/
From: Steven Haflich
Subject: Re: funcallable-standard-class question
Date: 
Message-ID: <M2klh.7974$yC5.4350@newssvr27.news.prodigy.net>
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.
From: Pascal Costanza
Subject: Re: funcallable-standard-class question
Date: 
Message-ID: <4vmurpF1crehmU1@mid.individual.net>
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/