From attila.lendvai at gmail.com Tue Jun 12 09:36:57 2007 From: attila.lendvai at gmail.com (Attila Lendvai) Date: Tue, 12 Jun 2007 11:36:57 +0200 Subject: [closer-devel] interesting contextl use-case Message-ID: hi! we would like to add new slots to a class when being inside a layer. the need comes when running some algorithm to calculate some new properties of some instances. the usual solution is to set up a hashtable, and add the extra property value in that hashtable. what we would like to do is to define new slots for a class but only in a given layer. so make-instance'ing that class when the layer is active should return instances with the extra slots. i'm not sure if this is already possible, but a possible implementation would be to delegate make-instance to make-layered-instance or make-instance-using-layer, then consult the layer context and as needed return an unnamed subclass of the actual class that contains the extra slots. thoughts? -- attila From pc at p-cos.net Tue Jun 12 18:02:38 2007 From: pc at p-cos.net (Pascal Costanza) Date: Tue, 12 Jun 2007 20:02:38 +0200 Subject: [closer-devel] interesting contextl use-case In-Reply-To: References: Message-ID: On 12 Jun 2007, at 11:36, Attila Lendvai wrote: > hi! > > we would like to add new slots to a class when being inside a layer. > the need comes when running some algorithm to calculate some new > properties of some instances. the usual solution is to set up a > hashtable, and add the extra property value in that hashtable. > > what we would like to do is to define new slots for a class but only > in a given layer. so make-instance'ing that class when the layer is > active should return instances with the extra slots. > > i'm not sure if this is already possible, but a possible > implementation would be to delegate make-instance to > make-layered-instance or make-instance-using-layer, then consult the > layer context and as needed return an unnamed subclass of the actual > class that contains the extra slots. > > thoughts? I don't fully understand what you are asking for, but let me make some guesses. ;) The basic functionality, as far as I see it, is already available: You can give partial definitions for the same layered class associated with different layers. This effectively allows you to define slots that are only visible when some layer is active. The basic approach looks like this: (define-layered-class my-object () ((foo :initarg :foo :layered-accessor foo))) (deflayer l1) (define-layered-class my-object :in-layer l1 () ((bar :initarg :bar :layered-accessor bar))) The effect of this definition is as follows: - The slot foo is always visible. - The slot bar can only be accessed via the accessor methods bar and (setf bar) when the layer l1 is active. - If you don't initialize a slot with a keyword argument (:foo or :bar) it will remain unbound (unless it has an :initform). In ContextL, the various slots from partial class definitions that contribute to a layered class are always allocated in instances of that class. However, as in CLOS, you always have control over whether they are actually bound or not. I have thought about making allocation of such slots variable, but I have decided against because (a) I don't expect the memory overhead to be substantial enough to warrant a more complex solution and (b) it's always easy to add optional slots with another metaclass that uses hashtables in the background (one of the standard CLOS MOP examples). It should be relatively straightforward to add this to ContextL at the user level. BTW, the restriction of access to slots coming from specific layers is only in effect for accessor (reader and writer) methods. slot- value and (setf slot-value) can always access slots, no matter what layer is active, because they are the low-level access mechanisms that are important for, for example, debuggers, inspectors, etc. If you indeed want to ensure that, based on the set of currently active layers, instances of a different class are created such that the slots from other non-active layers are definitely not present, then you can achieve this by implementing your own abstraction on top of CLOS / ContextL. Just implement your own layered function for creating instances, say create-instance, then you can define layered methods on it that change what kind of instance is actually created. It could have been possible to add such functionality to make- instance from within ContextL - however, modifying make-instance that way is probably not a good idea because CLOS implementations typically try to optimize instance creation as heavily as possible. Whenever you define methods on make-instance, there is a chance that you slow down the overall CLOS runtime. (That's just a guess, though, I could be wrong here.) I have probably not quite got the gist of what you actually want, but it's now your turn to clarify my misunderstandings. ;-) Cheers, Pascal -- Pascal Costanza, mailto:pc at p-cos.net, http://p-cos.net Vrije Universiteit Brussel, Programming Technology Lab Pleinlaan 2, B-1050 Brussel, Belgium From attila.lendvai at gmail.com Tue Jun 12 18:26:50 2007 From: attila.lendvai at gmail.com (Attila Lendvai) Date: Tue, 12 Jun 2007 20:26:50 +0200 Subject: [closer-devel] interesting contextl use-case In-Reply-To: References: Message-ID: > I don't fully understand what you are asking for, but let me make > some guesses. ;) yes, you did! :) > In ContextL, the various slots from partial class definitions that > contribute to a layered class are always allocated in instances of > that class. However, as in CLOS, you always have control over whether > they are actually bound or not. I have thought about making > allocation of such slots variable, but I have decided against because > (a) I don't expect the memory overhead to be substantial enough to > warrant a more complex solution and (b) it's always easy to add > optional slots with another metaclass that uses hashtables in the > background (one of the standard CLOS MOP examples). It should be > relatively straightforward to add this to ContextL at the user level. there are plenty of these CLOS objects (persisted in an RDBMS). and the algorithm i'm talking about is a report generation. so it's not a good idea for us to add each cache slot needed by the various report generators for each instance, whether it was created while inside the report generation or not. but on the other hand we don't want make-instance to be slow either, so eventually we'll dig deeper into this. for now the hashtables will do. but i would be very grateful if you could give me some links/hints on how the hashtable based slots work! i always thought that it's a deficiency of CLOS that the slot accessor atom (namely SLOT-VALUE-USING-CLASS) is dispatching on the effective slot instance, so there's no way to create a CLOS metaclass that has a working CL:SLOT-VALUE and can have a variable number of slots stored in a hashtable; e.g. the set of slot names not know at definition time. and thanks for the detailed answer! -- attila From pc at p-cos.net Tue Jun 12 18:44:28 2007 From: pc at p-cos.net (Pascal Costanza) Date: Tue, 12 Jun 2007 20:44:28 +0200 Subject: [closer-devel] interesting contextl use-case In-Reply-To: References: Message-ID: <6755FA4F-F53C-4399-B6A9-9EDB55084A67@p-cos.net> On 12 Jun 2007, at 20:26, Attila Lendvai wrote: >> I don't fully understand what you are asking for, but let me make >> some guesses. ;) > > yes, you did! :) :) > i would be very grateful if you could give me some links/hints on > how the hashtable based slots work! i always thought that it's a > deficiency of CLOS that the slot accessor atom (namely > SLOT-VALUE-USING-CLASS) is dispatching on the effective slot instance, > so there's no way to create a CLOS metaclass that has a working > CL:SLOT-VALUE and can have a variable number of slots stored in a > hashtable; e.g. the set of slot names not know at definition time. The examples in AMOP indeed go through slot-value-using-class, but the CLOS MOP was changed in the last minute before publication of the book to dispatch on slot definition metaobjects instead of slot names. So doing this via slot-value-using-class has indeed limitations. The trick is to go through slot-missing instead. You don't even need your own metaclass, a simple mixin class is sufficient. Here is some rough code that shows how to do it: (defclass hash-slots-object () ((hash-slots :accessor hash-slots :initform (make-hash-table :test #'eq)))) (defmethod slot-missing ((class t) (object hash-slots-object) slot-name operation &optional new-value) (ecase operation (slot-value (multiple-value-bind (value present-p) (gethash slot-name (hash-slots object)) (if present-p value (slot-unbound class object slot-name)))) (setf (setf (gethash slot-name (hash-slots object)) new-value)) (slot-boundp (nth-value 1 (gethash slot-name (hash-slots object)))) (slot-makunbound (remhash slot-name (hash-slots object))))) slot-missing is part of ANSI Common Lisp, so this is fully portable code. I hope this helps. Pascal -- Pascal Costanza, mailto:pc at p-cos.net, http://p-cos.net Vrije Universiteit Brussel, Programming Technology Lab Pleinlaan 2, B-1050 Brussel, Belgium From attila.lendvai at gmail.com Tue Jun 12 18:50:22 2007 From: attila.lendvai at gmail.com (Attila Lendvai) Date: Tue, 12 Jun 2007 20:50:22 +0200 Subject: [closer-devel] interesting contextl use-case In-Reply-To: <6755FA4F-F53C-4399-B6A9-9EDB55084A67@p-cos.net> References: <6755FA4F-F53C-4399-B6A9-9EDB55084A67@p-cos.net> Message-ID: > slot-missing is part of ANSI Common Lisp, so this is fully portable > code. > > I hope this helps. brilliant, thanks a lot! -- attila