From pc at p-cos.net Thu Mar 2 16:07:38 2006 From: pc at p-cos.net (Pascal Costanza) Date: Thu, 2 Mar 2006 17:07:38 +0100 Subject: Slots in layers, was Re: [closer-devel] ContextL design question... In-Reply-To: <985E7823-5C7A-4B0E-87AD-94BF4973B333@p-cos.net> References: <17e6dce0602271010p6b452b2eu2a46dc0de9b51401@mail.gmail.com> <17e6dce0602280159i4d6d4b95m9812865d711706bd@mail.gmail.com> <2C30E3AD-F004-4C85-BF25-4E7FFBBBD468@p-cos.net> <17e6dce0602281148s64f78c98mc59eecb58318b2db@mail.gmail.com> <985E7823-5C7A-4B0E-87AD-94BF4973B333@p-cos.net> Message-ID: <71540324-71C4-4726-88B3-57971FB711C8@p-cos.net> Hi Nick and everyone else, On 28 Feb 2006, at 22:06, Pascal Costanza wrote: > On 28 Feb 2006, at 20:48, Nick Bourner wrote: > >> Thanks for that, I'll give it a try. >> >> Is the value of the slot global or can it be set on a per-thread >> basis >> like active layers? I was thinking that it could be a good way of >> carrying information for a particular activation of a layer. Say, for >> instance, a stream for a display layer to write to or something. Then >> you could have something like >> >> (with-active-layers ((some-layer :some-slot a-value) some-other- >> layer) >> ...) >> or >> (ensure-active-layer (some-layer :some-slot a-value)) >> >> rather than something like >> >> (let ((*some-special* a-value)) >> (with-active-layers (some-layer some-other-layer) >> .... >> >> Not a whole lot different but I think it just expresses the intent >> slightly more clearly. > > Now, that's a very neat idea, especially the syntax. And I think it > should be possible to implement this in a relatively > straightforward way. The darcs repository now contains a new version of ContextL that supports this, with some slight differences though. You can now define layers like this: (deflayer some-layer () ((slot1 :initform 'foo :reader slot1) (slot2 :initform 'bar :special t :accessor slot2))) Since layers are singletons (I referred to this previously as the "prototype model"), you cannot associate an :allocation other than :class with a slot. If you don't specify :allocation, it will automatically be :class. Since you cannot make instances of a layer, you cannot specify any :initarg options either. Slots in layers can be :special, like in the metaclass special-class. (The :special option now also works correctly with :allocation :class, I hope - there was a bug in the special- class implementation in that regard that I have fixed.) I haven't extended the with-active-layers construct, though. Here are the reasons: - It would really only look neat when used with initargs instead of accessors. However, the mapping of initargs to actual slots can be changed at runtime in CLOS, so I would have to emit code with quite some overhead here that would determine the current mapping of an initarg to its actual slot. Worse, an initarg could actually be mapped to more than one slot, which would complicate the code even further, with very little reward. I wanted to avoid that. Therefore, if you want to switch layers and then also rebind slots in layers, you have to express it as follows: (with-active-layers (some-layer) (dletf (((slot1 (find-layer 'some-layer)) 42)) ...)) I hope this is neat enough. ;) Ah, yes, you don't need to say (layer-prototype (find-layer 'some- layer)) here. The singleton stuff is actually implemented in a metaclass singleton- class. So you can also create singleton classes like this: (defclass some-singleton-class () ((some-slot :initform 'baz :reader some-slot)) (:metaclass singleton-class)) ...and then you can access the slot also by saying (some-slot (find- class 'some-singleton-class)) or (slot-value (find-class 'some- singleton-class) 'some-slot). Singleton classes don't support special slots by themselves, you have to mix them with the metaclass special- class to get both functionalities. Please don't say find-class in conjunction with layers - this won't work! OK, I hope this will all work for you. Feel free to ask any questions... 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 drewc at tech.coop Fri Mar 3 02:42:05 2006 From: drewc at tech.coop (Drew Crampsie) Date: Thu, 02 Mar 2006 21:42:05 -0500 Subject: Slots in layers, was Re: [closer-devel] ContextL design question... In-Reply-To: <71540324-71C4-4726-88B3-57971FB711C8@p-cos.net> References: <17e6dce0602271010p6b452b2eu2a46dc0de9b51401@mail.gmail.com> <17e6dce0602280159i4d6d4b95m9812865d711706bd@mail.gmail.com> <2C30E3AD-F004-4C85-BF25-4E7FFBBBD468@p-cos.net> <17e6dce0602281148s64f78c98mc59eecb58318b2db@mail.gmail.com> <985E7823-5C7A-4B0E-87AD-94BF4973B333@p-cos.net> <71540324-71C4-4726-88B3-57971FB711C8@p-cos.net> Message-ID: <4407ACFD.7070505@tech.coop> Pascal Costanza wrote: > > I haven't extended the with-active-layers construct, though. Here are > the reasons: > - It would really only look neat when used with initargs instead of > accessors. However, the mapping of initargs to actual slots can be > changed at runtime in CLOS, so I would have to emit code with quite > some overhead here that would determine the current mapping of an > initarg to its actual slot. Worse, an initarg could actually be > mapped to more than one slot, which would complicate the code even > further, with very little reward. I wanted to avoid that. Sounds like my WITH-SPECIAL-INITARGS construct, which has all the drawbacks you mentioned, but simplifies the implementation of Lisp-on-Lines significantly. it's used like this like this : (with-special-initargs (object :some-initarg value :some-other-initarg another-value) ...) Below is the implementation. Use it however you want :) (defmethod initargs.slot-names (object) "Returns ALIST of (initargs) . slot-name." (nreverse (mapcar #'(lambda (slot) (cons (closer-mop:slot-definition-initargs slot) (closer-mop:slot-definition-name slot))) (closer-mop:class-slots (class-of object))))) (defun find-slot-names-from-initargs-plist (object initargs-plist) "returns (VALUES SLOT-NAMES VALUES), Given a plist of initargs such as one would pass to :DEFAULT-INITARGS. SLOT-NAMES contains the slot-names specified by the initarg, and VALUES the corresponding VALUE." (let (slot-names values (initargs.slot-names-alist (initargs.slot-names object))) (loop for (initarg value) on initargs-plist do (let ((slot-name (cdr (assoc-if #'(lambda (x) (member initarg x)) initargs.slot-names-alist)))) (when slot-name ;ignore invalid initargs. (good idea/bad idea?) (push slot-name slot-names) (push value values))) finally (return (values slot-names values))))) (defun funcall-with-special-initargs (object initargs function &rest args) "Call FUNCTION with dynnamic bindings of the slots in OBJECT specified by the INITARGS plist" (multiple-value-bind (slot-names values) (find-slot-names-from-initargs-plist object initargs) (special-symbol-progv (with-symbol-access (loop for slot-name in slot-names collect (slot-value object slot-name))) values (apply function args)))) (defmacro with-special-initargs ((object &rest initargs) &body body) `(funcall-with-special-initargs ,object ,initargs #'(lambda () , at body))) From pc at p-cos.net Fri Mar 3 16:30:45 2006 From: pc at p-cos.net (Pascal Costanza) Date: Fri, 3 Mar 2006 17:30:45 +0100 Subject: Slots in layers, was Re: [closer-devel] ContextL design question... In-Reply-To: <4407ACFD.7070505@tech.coop> References: <17e6dce0602271010p6b452b2eu2a46dc0de9b51401@mail.gmail.com> <17e6dce0602280159i4d6d4b95m9812865d711706bd@mail.gmail.com> <2C30E3AD-F004-4C85-BF25-4E7FFBBBD468@p-cos.net> <17e6dce0602281148s64f78c98mc59eecb58318b2db@mail.gmail.com> <985E7823-5C7A-4B0E-87AD-94BF4973B333@p-cos.net> <71540324-71C4-4726-88B3-57971FB711C8@p-cos.net> <4407ACFD.7070505@tech.coop> Message-ID: <9D8E8DCE-1595-4492-81A0-F43692E61A66@p-cos.net> On 3 Mar 2006, at 03:42, Drew Crampsie wrote: > Pascal Costanza wrote: > >> I haven't extended the with-active-layers construct, though. Here >> are the reasons: >> - It would really only look neat when used with initargs instead >> of accessors. However, the mapping of initargs to actual slots >> can be changed at runtime in CLOS, so I would have to emit code >> with quite some overhead here that would determine the current >> mapping of an initarg to its actual slot. Worse, an initarg could >> actually be mapped to more than one slot, which would complicate >> the code even further, with very little reward. I wanted to avoid >> that. > > Sounds like my WITH-SPECIAL-INITARGS construct, which has all the > drawbacks you mentioned, but simplifies the implementation of Lisp- > on-Lines significantly. OK, I will try to integrate this somehow. In order to make this work, I have started to add the opportunity to declare :initarg options for slots in deflayer forms. However, this has revealed a few bugs with regard to special slots with :allocation :class. I have fixed those bugs and submitted the changes to the darcs repository. Here are some important remarks: - :initarg options are now indeed supported in deflayer forms. - special slots with :allocation :class now work correctly, especially in conjunction with initialize-instance, reintialize- instance, shared-initialize, etc. This has led to serious bugs before. - Singleton classes had an implementation that allowed you to automagically access :allocation :class slots of a class without explicitly mentioning its class prototype. This was a bad idea because those slots could be mixed up with actual instance slots of the metaclass. So I have removed the magic. Still, I wanted to make it straightforward to access slots in layers (as specified in deflayer forms), which are internally turned into :allocation :class slots. I have achieved this by renaming a few functions: - The previous find-layer function is now called find-layer-class, and returns the layer class. - The new find-layer function now returns the singleton / prototype instance of a layer class. - layer-prototype doesn't exist anymore. The new find-layer / find-layer-class are quite flexible: You can pass them layer names, layer classes or layer class prototypes, and they will return the right thing. So the following code should work: (deflayer test-layer () ((s0 :initarg :s0 :initform 'foo :special t :reader s0) (s1 :initarg :s1 :initform 'bar :reader s1))) (s0 (find-layer 'test-layer)) => 'foo (s1 (find-layer 'test-layer)) => 'bar etc. Something like with-special-initargs or with-active-layers that allows you to use initargs for slots in layers doesn't exist yet. That's one of the next items on my todo list. 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 pc at p-cos.net Sun Mar 5 12:42:02 2006 From: pc at p-cos.net (Pascal Costanza) Date: Sun, 5 Mar 2006 13:42:02 +0100 Subject: [closer-devel] with-special-initargs, etc. Message-ID: <2585FBAF-22FF-42A7-9C2B-CD3997101A43@p-cos.net> Hi, OK, there is a new version of ContextL in the darcs repository with the following extensions: - with-special-initargs allows you to rebind slots in objects via initargs. The syntax looks as follows: (with-special-initargs ((object1 :initarg1 value1 :initarg2 value2) (object2 :initarg3 value3 :initarg4 value4)) ... body ...) All the objects, initargs and values are in evaluated positions. Initialization goes through shared-initialize so that you can do further customizations. There is also a with-special-initargs*: (with-special-initargs* ((object1 ...) (object2 ...)) ... body ...) The difference is that in with-special-initargs, the objects, initargs and values are evaluated before any rebinding is performed, while in with-special-initargs*, first object1 is reinitialized with dynamic scoped, then object2, and so forth. - with-active-layers is extended accordingly. If you only specify layer names, then it works as before: (with-active-layers (a b c) ...) However, you can also specify initargs to rebind slots in layers with dynamic scope: (with-active-layers ((a :initarg1 value1) b (c :initarg2 value2)) ...) This expands roughly to: (with-active-layers (a b c) (with-special-initargs (((find-layer 'a) :initarg1 value1) ((find-layer 'c) :initarg2 value2)) ...)) There is also a with-active-layers* that expands to a similar form but with with-special-initargs* accordingly. I haven't tested these extensions extensively, but I am pretty sure that they should work correctly. This includes the fact that initargs that are valid for multiple slots will also rebind all of the corresponding slots. Only slots that are :special will ever be rebound by the extensions above. Currently, I don't signal any warnings or errors if an initarg (also) corresponds to a slot that is not special - such a slot is simply left out. Please let me know if you prefer to see an error in this case. 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 pc at p-cos.net Sun Mar 5 14:24:31 2006 From: pc at p-cos.net (Pascal Costanza) Date: Sun, 5 Mar 2006 15:24:31 +0100 Subject: [closer-devel] Re: Layered function troubles In-Reply-To: <17e6dce0602200910j150a7746safd9b431d74ba58a@mail.gmail.com> References: <17e6dce0602200552o55f31eecy8d59ffc0716fcbb@mail.gmail.com> <17e6dce0602200910j150a7746safd9b431d74ba58a@mail.gmail.com> Message-ID: On 20 Feb 2006, at 18:10, Nick Bourner wrote: > Hmm. In case anyone else has a similar problem, the blindingly > stupid mistake was sloppy programming on my part. I had assumed > that define-layered-method worked the same as defmethod, so I could > get away without using define-layered-function first because define- > layered-method would do it for me, especially since I got a warning > saying that the generic function had implicitly been created. Bad > assumption. It all works fine as long as you remember to use define- > layered-function to create the generic first. I have tried to make it work that you don't need so say (define- layered-function ...), but unfortunately, the CLOS MOP does not allow me to express this - at least, it's not obvious to me how to do that. (There are some subtleties wrt the :argument-precedence-order argument to ensure-generic-function that are not that nice to deal with...) Sorry... 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 nickb42 at gmail.com Sun Mar 5 19:56:27 2006 From: nickb42 at gmail.com (Nick Bourner) Date: Sun, 5 Mar 2006 19:56:27 +0000 Subject: [closer-devel] Re: Layered function troubles In-Reply-To: References: <17e6dce0602200552o55f31eecy8d59ffc0716fcbb@mail.gmail.com> <17e6dce0602200910j150a7746safd9b431d74ba58a@mail.gmail.com> Message-ID: <17e6dce0603051156g28e5280do5cb7ab9d7b6b1151@mail.gmail.com> On 3/5/06, Pascal Costanza wrote: > > I have tried to make it work that you don't need so say (define- > layered-function ...), but unfortunately, the CLOS MOP does not allow > me to express this - at least, it's not obvious to me how to do that. > (There are some subtleties wrt the :argument-precedence-order > argument to ensure-generic-function that are not that nice to deal > with...) > > Sorry... > No worries, it wasn't a big problem. I just thought it would be useful to note it somewhere visible in case anyone else hit the same problem. Nick From nickb42 at gmail.com Sun Mar 5 20:16:59 2006 From: nickb42 at gmail.com (Nick Bourner) Date: Sun, 5 Mar 2006 20:16:59 +0000 Subject: [closer-devel] Slot definition inheritance Message-ID: <17e6dce0603051216q6cb0325ar4ee268938c2cad4f@mail.gmail.com> Hi all, Not sure this is entirely on topic here, but it's a MOP question and I'm using closer-mop :-) Does anyone know how (or indeed if it is possible) to have a slot inherit from two different slot definitions, so that the effective slot definition is the union of the two direct slot definitions. There's code in section 3.5.2 of AMOP that seems like it would be possible by making an effective slot definition on the fly using "make-effective-slot-definition" but that doesn't seem to exist in SBCL or OpenMCL, or at least it isn't exported from wherever it's hiding. I have a suspicion I'm going to have to do this by making one slot definition a subclass of the other, but something like the AMOP stuff would sure be handy. Anyone know for sure? Thanks, Nick From pc at p-cos.net Tue Mar 7 16:22:42 2006 From: pc at p-cos.net (Pascal Costanza) Date: Tue, 7 Mar 2006 17:22:42 +0100 Subject: [closer-devel] Slot definition inheritance In-Reply-To: <17e6dce0603051216q6cb0325ar4ee268938c2cad4f@mail.gmail.com> References: <17e6dce0603051216q6cb0325ar4ee268938c2cad4f@mail.gmail.com> Message-ID: <492E9296-DC9F-4C08-9DDE-2895668FDF17@p-cos.net> On 5 Mar 2006, at 21:16, Nick Bourner wrote: > Hi all, > > Not sure this is entirely on topic here, but it's a MOP question and > I'm using closer-mop :-) > > Does anyone know how (or indeed if it is possible) to have a slot > inherit from two different slot definitions, so that the effective > slot definition is the union of the two direct slot definitions. > There's code in section 3.5.2 of AMOP that seems like it would be > possible by making an effective slot definition on the fly using > "make-effective-slot-definition" but that doesn't seem to exist in > SBCL or OpenMCL, or at least it isn't exported from wherever it's > hiding. > > I have a suspicion I'm going to have to do this by making one slot > definition a subclass of the other, but something like the AMOP stuff > would sure be handy. > > Anyone know for sure? Yes - that function doesn't exist. The AMOP book uses different variations of a metaobject protocol throughout the book to make certain points clear. The only specification that's actually somewhat binding for CLOS are chapters 5 and 6. There's no "make-effective-slot-definition" mentioned there. BTW, an effective slot definition class shouldn't inherit from a direct slot definition class. Direct slot definitions are slots as they (conceptually) occur in a defclass form, while effective slot definitions are slots as they are inherited and possibly combined from a class and all its superclasses. It's important to keep these concepts separate. A function that creates a subclass on the fly based on a list of classes to be inherited from could actually useful in other circumstances as well. So something like (make-combined-instance '(a b c) ...) could create a class with the direct superclasses a, b and c and then create an instance of that class. It would probably make sense to cache such implicitly created classes based on the list of direct superclasses in order to reuse existing combinations and not to waste too much space by creating too many of those combination classes. 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 gwking at metabang.com Tue Mar 7 16:49:18 2006 From: gwking at metabang.com (Gary King) Date: Tue, 7 Mar 2006 11:49:18 -0500 Subject: [closer-devel] Slot definition inheritance In-Reply-To: <492E9296-DC9F-4C08-9DDE-2895668FDF17@p-cos.net> References: <17e6dce0603051216q6cb0325ar4ee268938c2cad4f@mail.gmail.com> <492E9296-DC9F-4C08-9DDE-2895668FDF17@p-cos.net> Message-ID: FWIW, there is a not perfect implementation of this sort of thing in the metabang-dynamic-classes ASDF system (which is really part of metatilities. It includes simple-define-class and define-class. Both of these just create classes and don't worry about caching. It also includes a system to map initargs to mixins which can be used to build classes as necessary. These are cached. As an example, here is some code from CL-Containers. First, I tell dynamic-classes which subclasses are necessary for various initargs: (add-parameter->dynamic-class :iterator :transform 'transforming- iterator-mixin) (add-parameter->dynamic-class :iterator :filter 'filtered-iterator- mixin) (add-parameter->dynamic-class :iterator :unique 'unique-value- iterator-mixin) (add-parameter->dynamic-class :iterator :circular 'circular-iterator- mixin) Now when I make an iterator, I get a different class depending on what I ask for... (class-of (make-iterator '(1 2 3 2 4 5) :filter #'oddp)) ==> # (make-iterator '(3 2 4 1 3 2) :filter #'oddp :unique t) ==> # I don't have a great sense of what the cost of this machinery is (though it is small enough that I use it quite a bit without feeling any pain). The package can be downloaded via ASDF-Install or found on metatilities home page (http://common-lisp.net/project/cl-containers/ metatilities/). If you do look at the code, it will be obvious that it is a somewhat decrepit state. I hope to remedy that sometime real soon. If there is any interest, real soon may occur faster than if not! HTH On Mar 7, 2006, at 11:22 AM, Pascal Costanza wrote: > > could create a class with the direct superclasses a, b and c and > then create an instance of that class. It would probably make sense > to cache such implicitly created classes based on the list of > direct superclasses in order to reuse existing combinations and not > to waste too much space by creating too many of those combination > classes. -- Gary Warren King metabang.com http://www.metabang.com/ From pc at p-cos.net Fri Mar 10 16:25:23 2006 From: pc at p-cos.net (Pascal Costanza) Date: Fri, 10 Mar 2006 17:25:23 +0100 Subject: [closer-devel] Metacircular layer activation Message-ID: Hi, I have finally added a metaobject protocol to ContextL. The changes are as follows: - A layer is an instance of a ContextL-specific metaclass. By default, it is the metaclass standard-layer-class. You can derive your own metaclasses from that class and declare a layer definition to be an instance of your new metaclass. For example: (deflayer some-layer (... some super layers ...) (... some slots ...) (:layer-class my-layer-class)) - There are two layered functions activate-layer-using-class and deactivate-layer-using-class. You can specialize them and/or put them in your own layers. All layer activations/deactivations go through those layered functions. For example: (define-layered-method activate-layer-using-class :in-layer some-layer ((layer my-layer-class) active-layers) ...) These layered functions return two values: A new list of active layers and a boolean flag indicating whether the result can be cached or not. If the second return value is true, activate-layer-using- class won't be called anymore for the same list of active layers, but the previously computed value will be reused. The same holds for deactivate-layer-using-class. See the file grouped-layers.lisp in the test folder in the darcs repository for an example of how to use those two layered functions. There is not a lot what you can do with the second parameter (active- layers) apart from passing it (implicitly or explicitly) to call-next- method. I may add more functionality here in the future. Suggestions are welcome. - Important: In order to make this metacircular implementation work, I had to change the semantics of ContextL somewhat. Consider the following activations of layers: (with-active-layers (a) (with-active-layers (b) (with-active-layers (a) ... contained code ... ))) Until recently, the contained code has seen the layers a and b active in that order. This was because an activation of an already active layer has lead to a deactivation of that layer with an immediate reactivation, so that it was guaranteed that that layer would be in front of all other layers. Since today, this has changed. Now the contained code sees the layer b and a active in that order. In other words, it is just checked whether the layer is already active somewhere, and if so, the list of active layers will remain unchanged. This was important in order to get layer activation/deactivation right in conjunction with inheritance between layers. I hope this doesn't break any existing code. If you need the previous behavior, it should be possible to get it to work by specializing activate-layer-using-class and deactivate-layer-using-class. (Cool, eh? ;) 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