Discussion:
slots on layers
Attila Lendvai
2008-08-18 21:06:39 UTC
Permalink
dear list,

from time to time we are returning to an important use-case that can't
(?) be done using contextl currently. what we need is slots on the
layer prototype that keep the expected semantics when layers are
restored using (current-layer-context) and (funcall-with-layer-context
...).

the problem is that if a non-special slot is defined for a layer, then
it's shared between all threads (in the slot of the single cached
prototype). but :special t slots won't work as expected either,
because they store their values in special bindings which are lost
when going through c-l-c/f-w-l-c.

our real-world use-case is this: we have a bunch of factory methods
that build up a gui component hierarchy. this algorithm is driven by
the metadata of the data model of the application. this metadata has
entities, typed properties, associations, etc, so if you define a data
model then in return you get a 90% gui that can be used to navigate
and edit the data.

to fill in the remaining 10% for each project, we need to customize
this algorithm (a bunch of layered methods), which is achived using
layers. it serves well most of the time, but often at the entry points
of this algorithm we need to store some lexically available
information in the layer instance to make it accessible deep inside
the recursive algorithm where the customized methods of the layer get
called.

the problem: the gui built by this algorithm is huge/infinite in
various directions, so it's built lazily as the data graph is
navigated. when the user clicks something in the browser, the server
potentially needs to extend the component graph by invoking some
closures. we restore the layer context when those closures are called,
but the slots of the layers are not restored.

the current contextl implementation tries hard to speed up layer
activation. for us a much simpler implementation would suffice: the
layer instance would not only be a prototype but a full instance which
is instantiated at each layer activation/deactivation and the value of
the remained slots copied over from the parent layer instance. the
instance could be directly rebound in the *layer* variable. also note
that this alternative implementation could fall back to using
prototypes when the layer has no slots (which is probably most of
the deployed usages. users, speak up! :)

we understand that this can be much slower when it comes to changing
the current effective layer, but we are not sure if this really counts
when it comes to the overall application performance. we don't know
how others are using contextl but in our usages the number of changes
to the active layers is negligable compared to the rest of the
runtime.

any thoughts?

--
attila

PS: a simple example:

CONTEXTL> (deflayer foo2 ()
((slot :initform 42 :accessor slot-of :initarg :slot :special t)))
CONTEXTL> (mapcar #'slot-of
(with-active-layers ((foo2 :slot 2))
(list (layer-context-prototype (current-layer-context))
(with-active-layers ((foo2 :slot 3))
(layer-context-prototype
(current-layer-context))))))

it returns (42 42) while we expect here to get (2 3). this effectively
means that we can't reinstate layer contexts that have slots.
Pascal Costanza
2008-08-20 10:38:27 UTC
Permalink
Hi Atilla,

Thanks a lot for your question and detailed explanation of your use
case. Such feedback is very helpful in understanding the limitations
of ContextL.

I have a question, see a problem with your suggested approach, and
think there is already a way in ContextL to achieve what you want.
However, I am not 100% sure, so please don't consider this as the
final word in this regard, but rather as a starting point for
continuing the discussion.

The question: I understand that you want to be able to reuse the state
of layer-specific slots from deactivated layers when you activate such
layers again. However, I don't quite understand how you expect to
activate that specific layer instance. Do you expect to be able to
inquire about which layers are currently active, and then pick out the
one you're interested in? The problem here is that in the general
case, the one layer you're interested in is somewhere in the list of
active layers, and not necessarily at the start or the end of the
list. So what criteria do you want to use to pick out the one specific
layer you're interested in? Or do you just want to refer to a layer
name? (This may seem the obvious way, but actually makes it harder for
me to understand what you really want/need, that's why I'm asking the
question...)

The problem (probably related to the question): When you pick out the
class prototype of the currently active layer combination (!), it is
an instance of an automatically generated class that hass all the
active layers as superclasses. So, for example, if layers l1, l2 and
l3 are active in that order, the class prototype is an instance of a
class %l123 that has l1, l2 and l3 as superclasses. Now, assume that
you are interested in the layer-specific state of l3, so you want to
pick out an instance that represents that state. It would have to be
an instance of the class %l123 as well, in order to ensure that
context-oriented dispatch works correctly. However, it could be that
in a new situation where you want to activate l3 again, there is a
different set of other layers currently active, say l2 and l4. The
eventual class prototype that represents the set of active layers
after reactivation of l3 should now be an instance of a class %l243,
so a class different from %l123. This automatically means that you
_cannot_ reuse that old instance of %l123. So in other words, reusing
the same class prototype from a previous activation state is _not_ a
good idea (unless you perform a change-class on that instance, but I
have strong reservations with regard to my willingness to deal with
the complexities that would arise from this ;).

An alternative solution: Why don't you just use layer inheritance? You
can then use layer-specific slots (whether they are :special or not)
in sublayers of the respective layers you are interested in.

As an example:

(deflayer foo3)

(deflayer sub1foo3 (foo3)
((some-slot :initform 2)))

(deflayer sub2foo3 (foo3)
((some-slot :initform 3)))

...etc., for as many sublayers as you want. The idea here is that you
use layers as prototypical objects (in the sense of languages like
Self or JavaScript) which can directly inherit from each other. You
then "just" have to ensure that you activate the correct sublayers for
the various contexts you're interested in. It should actually also be
possible to use anonymous layers which are generated at runtime
(although that part of the ContextL API has not seen extensive testing
in real-world use so far, to the best of my knowledge).

Would that get you closer (ha!) to a working solution for your problem?

Best,
Pascal

P.S.: I don't want to avoid changes to ContextL, it's just that I want
to make sure that changes to ContextL fit well with the rest of the
design of ContextL, that's why I'm yet hesitating to adopt your
suggested solution. Again, thanks a lot for your feedback, it is very
valuable!
Post by Attila Lendvai
dear list,
from time to time we are returning to an important use-case that can't
(?) be done using contextl currently. what we need is slots on the
layer prototype that keep the expected semantics when layers are
restored using (current-layer-context) and (funcall-with-layer-context
...).
the problem is that if a non-special slot is defined for a layer, then
it's shared between all threads (in the slot of the single cached
prototype). but :special t slots won't work as expected either,
because they store their values in special bindings which are lost
when going through c-l-c/f-w-l-c.
our real-world use-case is this: we have a bunch of factory methods
that build up a gui component hierarchy. this algorithm is driven by
the metadata of the data model of the application. this metadata has
entities, typed properties, associations, etc, so if you define a data
model then in return you get a 90% gui that can be used to navigate
and edit the data.
to fill in the remaining 10% for each project, we need to customize
this algorithm (a bunch of layered methods), which is achived using
layers. it serves well most of the time, but often at the entry points
of this algorithm we need to store some lexically available
information in the layer instance to make it accessible deep inside
the recursive algorithm where the customized methods of the layer get
called.
the problem: the gui built by this algorithm is huge/infinite in
various directions, so it's built lazily as the data graph is
navigated. when the user clicks something in the browser, the server
potentially needs to extend the component graph by invoking some
closures. we restore the layer context when those closures are called,
but the slots of the layers are not restored.
the current contextl implementation tries hard to speed up layer
activation. for us a much simpler implementation would suffice: the
layer instance would not only be a prototype but a full instance which
is instantiated at each layer activation/deactivation and the value of
the remained slots copied over from the parent layer instance. the
instance could be directly rebound in the *layer* variable. also note
that this alternative implementation could fall back to using
prototypes when the layer has no slots (which is probably most of
the deployed usages. users, speak up! :)
we understand that this can be much slower when it comes to changing
the current effective layer, but we are not sure if this really counts
when it comes to the overall application performance. we don't know
how others are using contextl but in our usages the number of changes
to the active layers is negligable compared to the rest of the
runtime.
any thoughts?
--
attila
CONTEXTL> (deflayer foo2 ()
((slot :initform 42 :accessor slot-
of :initarg :slot :special t)))
CONTEXTL> (mapcar #'slot-of
(with-active-layers ((foo2 :slot 2))
(list (layer-context-prototype (current-layer-
context))
(with-active-layers ((foo2 :slot 3))
(layer-context-prototype
(current-layer-context))))))
it returns (42 42) while we expect here to get (2 3). this effectively
means that we can't reinstate layer contexts that have slots.
_______________________________________________
closer-devel mailing list
http://common-lisp.net/cgi-bin/mailman/listinfo/closer-devel
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Attila Lendvai
2008-08-20 13:09:55 UTC
Permalink
Post by Pascal Costanza
Hi Atilla,
hello Pascal,
Post by Pascal Costanza
Thanks a lot for your question and detailed explanation of your use case.
Such feedback is very helpful in understanding the limitations of ContextL.
my pleasure!
Post by Pascal Costanza
The question: I understand that you want to be able to reuse the state of
layer-specific slots from deactivated layers when you activate such layers
again. However, I don't quite understand how you expect to activate that
specific layer instance. Do you expect to be able to inquire about which
layers are currently active, and then pick out the one you're interested in?
what you wrote in your mail is consistent with my understanding of
contextl, so i think we have a misunderstanding here. what i want to
reactivate later is the instance made from the class representing the
currently active layer combination (and its slots), not just a single
layer "extracted" from it. i think dealing with specific layers in
layer context restoration would be against the whole idea of contextl.

some more background info: in our setup there are several different
layers. some are simple customizations like passive-components-layer:
it tells the gui algorithm to forget about actions below this
component; think of rendering components into transient tooltips -
there's no point in rendering actions that the user can't click
anyway...

some other layers are very specific customizations, specific to a
certain gui form _instance_!

e.g.: some action instantiates a metadata driven finder component. but
this finder will be used for selecting data instances, so we also want
to customize this finder to add a "select this instance" action into
the result of the lister component that it will instantiate when
showing the result of its search. obviously the added "select this
instance" action needs to know where it belongs to, but this action
won't even be created until some browser requests later: the layered
method adding this extra action for the search result list is called
only after the user clicks the "search" action on the finder. this
only happens some browser requests later.

this gui algorithm is recursive, so you need to "attach" this
customization to the specific finder component instance. if it's not
done, or the layer's slot is shared then if the user navigates somehow
to a different instance of the finder instantiated deeper in the
component recursion, then this customization will screw up the
workings of the two finder instances.

[i'm trying hard to make these examples clear, but unfortunately i'm
aware of the deficiencies... but keep in mind that his is a recursive
meta-gui that can display/edit a random data model in a browser, so
there are some inherent complexities...]
Post by Pascal Costanza
class different from %l123. This automatically means that you _cannot_ reuse
that old instance of %l123. So in other words, reusing the same class
prototype from a previous activation state is _not_ a good idea (unless you
perform a change-class on that instance, but I have strong reservations with
regard to my willingness to deal with the complexities that would arise from
this ;).
it may not be a good idea in certain situations, but in the factory
part of our lazy gui building algorithm this is just what we need: you
start out with a component representing/displaying a single instance
of your data graph. when creating the start component certain layers
are enabled to customize the view that starts our from here. the
current layer instance is stored (representing the full combination of
the current layer setup) where laziness stops the further creation of
child components using a lambda (this is the usual delay/force
laziness implemented using lambda's).

when the user later invokes an action that needs to extend the
component graph (by force-ing some delay-ed lambdas), we restore the
factory-time layer setup before calling the delayed lambda. e.g.: the
user clicks a link in the browser which is representing the remote
instance of an association. when it happens the link is replaced with
a detailed view of the target of the association (whatever the target
is... this is a component recursion point).

as of change-class: in the implementation we proposed in the previous
mail... when the layer setup is changed, then you need to _clone_ and
rebind the layer prototype instance, so i'd simply write a special
clone-layer-instance instead of change-class. of course this
interferes with certain shared-initialize customizations, but the
class defined by deflayer is private anyway.
Post by Pascal Costanza
An alternative solution: Why don't you just use layer inheritance? You can
in short: because we need to remember such data in layered methods
that was only available in the lexical scope of with-active-layer
(several browser requests ago). this implies that anonymous layers are
not good for this, because a new layer would need to be created for
each run of the code block.
Post by Pascal Costanza
P.S.: I don't want to avoid changes to ContextL, it's just that I want to
make sure that changes to ContextL fit well with the rest of the design of
ContextL, that's why I'm yet hesitating to adopt your suggested solution.
no worries, you didn't sound like that at all!
Post by Pascal Costanza
Again, thanks a lot for your feedback, it is very valuable!
a simpler but very similar example: think of a recursive graph
traversal algorithm that can be customized using contextl and can stop
and resume the traversal at various points. say it needs to traverse
potentially infinite graphs in the background and at various nodes it
needs the user's feedback to make decisions that affect the _rest_ of
the traversal but only on the rest of _that_ path.

so it needs to be able to stop and resume the traversal at multiple
nodes at the same time. whenever its stops, it needs a way to capture
the current dynamic context (including the layer instance and its
slots), so that it can be restored when the user comes back and anwers
the question that made the traversal on this path to halt.

if you add one more requirement, that some of the customizations are
parametrized, then you get into a situation that is similar to ours.

hth,
--
attila
Pascal Costanza
2008-08-24 11:58:14 UTC
Permalink
Post by Attila Lendvai
a simpler but very similar example: think of a recursive graph
traversal algorithm that can be customized using contextl and can stop
and resume the traversal at various points. say it needs to traverse
potentially infinite graphs in the background and at various nodes it
needs the user's feedback to make decisions that affect the _rest_ of
the traversal but only on the rest of _that_ path.
so it needs to be able to stop and resume the traversal at multiple
nodes at the same time. whenever its stops, it needs a way to capture
the current dynamic context (including the layer instance and its
slots), so that it can be restored when the user comes back and anwers
the question that made the traversal on this path to halt.
OK, let me try to hook into this example, because it seems simpler
than the other descriptions. Maybe we can bootstrap a solution from
here.

What you describe seems to be a classic scenario for using first-class
continuations. What I already have on my todo list for ContextL is
support for first-class dynamic closures. The idea would be that you
can say something like (capture-dynamic-bindings), which gives you a
first-class representation of all special variables, which you can
later reinstate by something like (with-reinstated-dynamic-
bindings ...). Such a dynamic closure facility would capture the
current representation of active layers (because that's in a special
variable), as well as, for example, special slots both in the layers
and in other objects.

(It won't be as general as that, I will have to require registering
the special variables that you want to see captured, but I think I can
make this relatively non-intrusive.)

Would that solve your problem?

I already know how to implement this in a portable way, and it
shouldn't affect the essential performance of ContextL. I just need a
couple of days of free time to do this...

What's your opinion? Am I still off of what you need?


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Attila Lendvai
2008-08-30 22:44:52 UTC
Permalink
Post by Pascal Costanza
What you describe seems to be a classic scenario for using first-class
continuations. What I already have on my todo list for ContextL is support
to be more precise what we need is only one-shot continuations, which
can simply be done with a lambda plus some macrology without much
overhead.
Post by Pascal Costanza
for first-class dynamic closures. The idea would be that you can say
something like (capture-dynamic-bindings), which gives you a first-class
representation of all special variables, which you can later reinstate by
something like (with-reinstated-dynamic-bindings ...). Such a dynamic
closure facility would capture the current representation of active layers
(because that's in a special variable), as well as, for example, special
slots both in the layers and in other objects.
(It won't be as general as that, I will have to require registering the
special variables that you want to see captured, but I think I can make this
relatively non-intrusive.)
Would that solve your problem?
i think it would, but i still don't see why it is worth it. what you
describe seems to be a lot more complex than if we cloned a new
instance at each change to the layer context. AFAICS, from the user's
POV the two solutions would be equivalent (except the different
performance characteristics), but the implementation of the
make-instance/copy-slots seems much more simple with potentially less
surprises.

as of the performance, these layer prototype instances would have
standard slots that are much faster to access, while layer activation
would be slower, but that's rare (at least in our use-cases). in fact
the entire contextl related performance impact on our application is
probably negligible, and probably most of it comes from the extra
dispatch parameter.

i don't want to say that first class dynamic closures are not useful
in general, but in this situation it feels like too much machinery.

we already have a more fine-grained protocol to restore the dynamic
environment when for example a partial ajax rendering happens
somewhere inside the component hierarchy (components can specialize
the call-in-component-environment method, which is called on the
parent path of the component, one by one). most, but not all of its
usage is restoring dynamic variable bindings that could be covered by
capture-dynamic-bindings, but the protocol is more flexible than that.
it costs us a dozen of extra method calls, but our main priority is
code maintainability/flexibility/reuse, not speed.

i'm sorry if i sounded negative, but i don't have a fine enough
english for this. and i'm just a user anyway, so take it all with a
piece of salt... :) i may even not see the whole picture or an
important detail.
--
attila
Pascal Costanza
2008-09-05 13:14:31 UTC
Permalink
Hi again,

This is a useful discussion, because it's about something that I
wanted to integrate into ContextL for some item anyway, and we can now
make some "right" decisions here.
Post by Attila Lendvai
Post by Pascal Costanza
What you describe seems to be a classic scenario for using first-
class
continuations. What I already have on my todo list for ContextL is support
to be more precise what we need is only one-shot continuations, which
can simply be done with a lambda plus some macrology without much
overhead.
OK, that sounds good. (Although I would like to make it work with more
full-fledged continuations as well.)
Post by Attila Lendvai
Post by Pascal Costanza
for first-class dynamic closures. The idea would be that you can say
something like (capture-dynamic-bindings), which gives you a first-
class
representation of all special variables, which you can later
reinstate by
something like (with-reinstated-dynamic-bindings ...). Such a dynamic
closure facility would capture the current representation of active layers
(because that's in a special variable), as well as, for example, special
slots both in the layers and in other objects.
(It won't be as general as that, I will have to require registering the
special variables that you want to see captured, but I think I can make this
relatively non-intrusive.)
Would that solve your problem?
i think it would, but i still don't see why it is worth it. what you
describe seems to be a lot more complex than if we cloned a new
instance at each change to the layer context. AFAICS, from the user's
POV the two solutions would be equivalent (except the different
performance characteristics), but the implementation of the
make-instance/copy-slots seems much more simple with potentially less
surprises.
True, that implementation seems more simple (although I think my
suggestion is not that complicated either, but I'm probably not
communicating it well enough at the moment).

I tend towards the first-class dynamic environments because it sounds
to me to be the "right" solution, whereas what you suggest seems very
specific to some (important) detail ContextL. My hope is that the
dynamic environments could be generalized in such a way that they
become useful outside of ContextL as well. They will probably be part
of the ContextL library, but it should be possible to use them without
using any of ContextL's other features. (It is my strong belief that
language constructs should be as independent of each other as possible.)
Post by Attila Lendvai
as of the performance, these layer prototype instances would have
standard slots that are much faster to access, while layer activation
would be slower, but that's rare (at least in our use-cases). in fact
the entire contextl related performance impact on our application is
probably negligible, and probably most of it comes from the extra
dispatch parameter.
I'm actually not so worried about performance either, it's just a nice
side effect if you can get good performance, but it's not on the top
of my priority list.
Post by Attila Lendvai
i don't want to say that first class dynamic closures are not useful
in general, but in this situation it feels like too much machinery.
I think I can make it work in such a way that it will feel very
seamless. My idea is that the current context parameter and any
special slots defined in layered and special classes will be
automatically captured by default, without you having to worry about
anything. I'm very certain that this should work (as always, it could
have nasty corner cases that I don't know about yet, of course).
Post by Attila Lendvai
we already have a more fine-grained protocol to restore the dynamic
environment when for example a partial ajax rendering happens
somewhere inside the component hierarchy (components can specialize
the call-in-component-environment method, which is called on the
parent path of the component, one by one). most, but not all of its
usage is restoring dynamic variable bindings that could be covered by
capture-dynamic-bindings, but the protocol is more flexible than that.
it costs us a dozen of extra method calls, but our main priority is
code maintainability/flexibility/reuse, not speed.
I am very interested to hear more about this, because this seems to be
a part where I can double check whether "my" dynamic environments are
general enough to support such things:

+ To what extent is your machinery more fine-grained? Can you select
which special variables are captured and which aren't? Or do you mean
something else here?

+ Do you have some source code publicly available where this is
provided and used? I would like to take a look at it, if you are fine
with that.
Post by Attila Lendvai
i'm sorry if i sounded negative, but i don't have a fine enough
english for this. and i'm just a user anyway, so take it all with a
piece of salt... :) i may even not see the whole picture or an
important detail.
Don't worry, I also don't have a complete view of the whole picture,
that's why it's good to have this discussion.


Best,
Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Drew Crampsie
2008-09-05 21:27:16 UTC
Permalink
Hey All,

I have a need for a mechanism similar to the one being described. I
use ContextL with continuations, and i already use something similar
to what Attila was describing. First some example code, with comments.

In LoL we use objects called "descriptions" to control how an object
is displayed. These are essentially layers with per-layer-slot-value
semantics implemented using anonymous layered generic functions.

(define-description bar ())

(define-description foo ()
((attribute-with-value :label "A value:" :value 'foo-value))

(define-description foo ()
((attribute-with-value :label "A different value:" :value 'foo-in-bar-value))
(:in-description bar))

(defun display-an (a)
(format t "~A ~A%" (attribute-label a) (attribute-value))

(let* ((description (find-description 'foo))
(attribute (find-attribute description attribute-with-value))

;; set up the environment to describe the object NIL using our description
(with-described-object (nil description)
;; So, executing :
(display-an attribute)
;; will print "A value: FOO-VALUE"

;; then if you do
(with-active-descriptions (bar)
(display-an attribute) )
;; you'll get "A different value: FOO-IN-BAR-VALUE"

;; this is implemented via a more general mechanism such that:

(with-active-descriptions (bar) (setf (slot-value attribute
'label) "A New Label"))
;; will set it permenantly in the bar description (layer)
)
(with-active-descriptions (bar) (attribute-label attribute))
;; => "A New Label"

This works well for the most part, but the inability to capture the
dynamic environment has lately become a problem. i've been able to
hack it up via manually capturing and re-instating the bindings i
need, but it's a hack and i'm sure there's a better way. Somethimg
Post by Pascal Costanza
for first-class dynamic closures. The idea would be that you can say
something like (capture-dynamic-bindings), which gives you a first-class
representation of all special variables, which you can later reinstate by
something like (with-reinstated-dynamic-bindings ...). Such a dynamic
closure facility would capture the current representation of active layers
(because that's in a special variable), as well as, for example, special
slots both in the layers and in other objects.
(It won't be as general as that, I will have to require registering the
special variables that you want to see captured, but I think I can make this
relatively non-intrusive
This is exactly what i need. I had a few ideas on how to implement it,
but it's not on my plate right now. I'd really like to see something
like this are part of contextL, as i'm sure it would be much more
thought-out than my implementation :).
I think I can make it work in such a way that it will feel very seamless. My
idea is that the current context parameter and any special slots defined in
layered and special classes will be automatically captured by default,
without you having to worry about anything. I'm very certain that this
should work (as always, it could have nasty corner cases that I don't know
about yet, of course).
I'de like to put my vote in for this solution, as it's very much in
line with how i use contextl. It would enable me to greatly simplify
my implementation in a lot of ways.

I'd be more than happy to get into greater detail, i'm very glad we're
having this discussion! :)

Cheers,

drewc
Attila Lendvai
2008-09-15 20:35:44 UTC
Permalink
True, that implementation seems more simple (although I think my suggestion
is not that complicated either, but I'm probably not communicating it well
enough at the moment).
my concerns are mostly related to debugging a random problem with this
machinery operating in the background. also i think that it's a middle
ground solution that on one hand has lost its simplicity and on the
other hand doesn't give a generic solution, either (see below)...
I am very interested to hear more about this, because this seems to be a
part where I can double check whether "my" dynamic environments are general
it's available at:
http://common-lisp.net/cgi-bin/darcsweb/darcsweb.cgi?r=cl-dwim-wui;a=summary

search for call-in-component-environment and
with-restored-component-environment.

starting the thing is more pain than it should be for now, especially
when you want to use the interesting part (the metagui that can
present a random cl-perec ER model).
+ To what extent is your machinery more fine-grained? Can you select which
special variables are captured and which aren't? Or do you mean something
else here?
it's basically a generic method called for each component in the
component path to the root, in a definite order. so you can bind
whatever you want in :around methods...

of course it means that it's more trouble to rebind variables with
this infrastructure than it would be with a dedicated dynamic
environment support. but on the other hand we can run code if needed,
as opposed to simply reinstating special bindings. and also it's a
specialized infrastructure related to components/gui's, nothing
generally usable.

btw, i think the generic solution to this problem would be to have
continuations (either delimited or not) with dynamic-wind support
(e.g. as in scheme). for me anything else feels as a specific solution
given to a certain use-case. in fact if i really needed this generic
language feature, then i would implement closure based CPS in
cl-delico (it only has an interpreter for now) and add dynamic-wind
support to it.

so, to sum up my current state of mind: i'd implement layer instance
modification using a specialized cloning (random sideffect: much
faster slot access on layers); do our specialized one-shot
continuations using some light macrology (as currently done); and if i
ever wanted to have all this in a generic way, then i'd extend
cl-delico with dynamic-wind.

but i'm right before falling asleep, so... :)
--
attila
Pascal Costanza
2008-10-02 13:57:30 UTC
Permalink
Just to give a note that I haven't forgotten about this: I have a
response for this email, but need a bit more time in a row to
formulate it well. Sorry for the delay, I hope to be able to post the
reply soon.

I expect to be able to implement what's necessary by end of October /
beginning of November. I now have a pretty good idea what to do that
should be useful for everyone, but I hope to be able to discuss this
before making the changes to (hopefully) get your acknowledgment.

Best,
Pascal
Post by Attila Lendvai
True, that implementation seems more simple (although I think my suggestion
is not that complicated either, but I'm probably not communicating it well
enough at the moment).
my concerns are mostly related to debugging a random problem with this
machinery operating in the background. also i think that it's a middle
ground solution that on one hand has lost its simplicity and on the
other hand doesn't give a generic solution, either (see below)...
I am very interested to hear more about this, because this seems to be a
part where I can double check whether "my" dynamic environments are general
http://common-lisp.net/cgi-bin/darcsweb/darcsweb.cgi?r=cl-dwim-wui;a=summary
search for call-in-component-environment and
with-restored-component-environment.
starting the thing is more pain than it should be for now, especially
when you want to use the interesting part (the metagui that can
present a random cl-perec ER model).
+ To what extent is your machinery more fine-grained? Can you
select which
special variables are captured and which aren't? Or do you mean something
else here?
it's basically a generic method called for each component in the
component path to the root, in a definite order. so you can bind
whatever you want in :around methods...
of course it means that it's more trouble to rebind variables with
this infrastructure than it would be with a dedicated dynamic
environment support. but on the other hand we can run code if needed,
as opposed to simply reinstating special bindings. and also it's a
specialized infrastructure related to components/gui's, nothing
generally usable.
btw, i think the generic solution to this problem would be to have
continuations (either delimited or not) with dynamic-wind support
(e.g. as in scheme). for me anything else feels as a specific solution
given to a certain use-case. in fact if i really needed this generic
language feature, then i would implement closure based CPS in
cl-delico (it only has an interpreter for now) and add dynamic-wind
support to it.
so, to sum up my current state of mind: i'd implement layer instance
modification using a specialized cloning (random sideffect: much
faster slot access on layers); do our specialized one-shot
continuations using some light macrology (as currently done); and if i
ever wanted to have all this in a generic way, then i'd extend
cl-delico with dynamic-wind.
but i'm right before falling asleep, so... :)
--
attila
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Pascal Costanza
2008-10-03 14:24:16 UTC
Permalink
Hi,

Back to this topic... ;)

I wanted to write a response where I comment on the fact that I find
dynamic-wind quite questionable - primarily because the dynamic
environments established with dynamic-wind are not first-class.
Dynamic-wind is actually also a controversial feature in the Scheme
community - it can be shown that call/cc with dynamic-wind can be
implemented in terms of call/cc without dynamic-wind, but not the
other way around, so in a 'minimal' language, dynamic-wind is actually
not necessary.

But after spending some more thought on this, I think I can propose a
solution that should make everybody happy.

Assume we have a function current-dynamic-environment and a macro with-
dynamic-environment with which you can get and set the current dynamic
environment. The idea is that specifically registered special
variables can be captured in such dynamic environments. For example,
all special slots in special classes, and the current layer context
will be such registered special variables. On top of that, it will be
possible to switch off support for dynamic environments completely
for people who really don't like them. (There will be a restriction
wrt side effects - assignments to such special variables will probably
not be recorded correctly in all situations. But that shouldn't be a
too strong restriction, because you typically rebind such variables
rather than set them. I will provide a detailed description when
assignments work and when they don't.)

Now, if you have support for call/cc in some other library, you can
add support for ContextL dynamic enviroments like this:

(defun dcall/cc (continuation)
(let ((dynamic-environment (current-dynamic-environment)))
(call/cc (lambda (k)
(funcall continuation (lambda (value)
(with-dynamic-environment
dynamic-environment
(funcall k value))))))))

It should also be possible to integrate support for both ContextL
dynamic environments and dynamic-wind into one construct, if necessary.

Next, there should be a way to support partial continuations. My
current idea is to have something like a function mark-dynamic-
environment that marks a portion of the current dynamic environment,
and that current-dynamic-environment can refer to such marks. So for
example, something like this:

(let ((mark (mark-current-dynamic-environment)))
...
... (current-dynamic-environment mark) ...
...)

The idea is that current-dynamic-environment captures the current
environment up until the specified mark (and if no mark is given, the
whole dynamic environment is captured).

There should then also be a form of with-dynamic-environment with
reestablishes such a partial dynamic environment - don't know exactly
how to do that yet.

Partial dynamic environments should make it easier to work correctly
with partial continuations, and since that's what you typically get
for Common Lisp, I think that's quite important.

So, do you think that the sketched solution covers the major concerns?


Pascal


--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium

Loading...