From pc at p-cos.net Sat Dec 27 11:13:24 2008 From: pc at p-cos.net (Pascal Costanza) Date: Sat, 27 Dec 2008 12:13:24 +0100 Subject: [closer-devel] Adding dynamic-wind to ContextL Message-ID: Hi everybody, I hope you all had a nice Christmas. And all the best for 2009! ;) I have spent the last couple of weeks on designing an interface for supporting first-class dynamic environments. This appears to be more complex than I initially thought - recording the values of (special) symbols representing dynamic variables is clearly not enough. I have stumbled over the following problem: In ContextL, the activation of layers amounts to roughly something like the following. There is a global *current-context* variable that records all the active layers as a list. So each layer activation corresponds to something like the following (strongly simplified). (let ((*current-context* (cons activated-layer *current-context*))) ...) Since I want to ensure that ContextL plays nice with the various Common Lisp frameworks for supporting first-class continuations, and since they actually implement partial continuations, I need a way to mark a position in the current control flow, and when I capture the current dynamic environment, I should actually only record those layer activations that were activated since a certain given mark. [1] I could theoretically record the mark in the *current-context* - so whenever a mark is set, I should also ensure that this is triggered for *current-context*: (let ((*current-context* (cons some-mark *current-context*))) ...) I could then search for that mark when capturing dynamic environments and only capture the "lower" part of the *current-context*. However, this doesn't work for deactivating layers. Deactivating layers looks roughly as follows: (let ((*current-context* (remove deactivated-layer *current-context*))) ...) There may be a way to mingle the layers and the marks in such a way that I can still get the right result, but this gets really messy. (I would have to record layer deactivations explicitly, by storing some kind of deactivation record in the current context, or so.) After some more thought about this whole problem, I came to the conclusion that providing a form of dynamic-wind, like in Scheme, is the better generalisation of dynamic environments. [2] So I plan to add the following to ContextL: + A with-dynamic-mark macro, that allows delimiting potential dynamic environments. The macro can be used like this: (with-dynamic-mark (some-mark-variable) some code) with-dynamic-mark delimits the current dynamic environment with a mark, and binds the some-mark-variable to a first-class representation of that mark (of unspecified type). Some-mark-variable can be lexically or dynamically scoped, which gives you static or dynamic delimited environments (corresponding to static or dynamic delimited continuations). + A dynamic-wind macro that allows setting up code that gets executed whenever a dynamic environment is (re)established. It is very roughly like Scheme's dynamic-wind, except that it isn't a function but a macro, that it doesn't provide the unwind-protect functionality (that would be redundant), and that it doesn't provide the set-up, main body, and tear-down code as separate thunks. The macro can be used like this: (dynamic-wind (let ((*current-context* (cons something *current-context*))) (proceed some code))) The code that is captured by dynamic-wind will just be executed as normal, but whenever a dynamic environment is reestablished that captures this dynamic-wind, the code here is re-executed up until the proceed form, which is then replaced with the continuation of reestablishing that dynamic environment. Proceed is like a progn, except that it marks the variable part of a dynamic-wind. You can combine this with unwind-protect: (let ((cell 42)) (dynamic-wind (let ((*some-variable* cell)) (unwind-protect (proceed with-something) (setq cell *some-variable*))))) This example ensures that *some-variable* is always bound to a given value in some dynamic environment, and also ensures that side effects to *some-variable* are handled correctly. A Scheme dynamic-wind, like this: (dynamic-wind set-up body tear-down) corresponds to a ContextL dynamic-wind as follows: (dynamic-wind (funcall set-up) (unwind-protect (proceed (funcall body)) (funcall tear-down))) [An advantage of this design of dynamic-wind is that I can completely remove the recording of dynamic-wind contexts if a client sets a flag that it doesn't want support for first-class dynamic environments.] + A capture-dynamic-environment function that allows capturing the current dynamic environment, either completely or up unto a certain dynamic mark. The function can be used like this: (capture-dynamic-environment mark) This returns a first-class representation of the dynamic environment up until the mark. If you want the complete dynamic environment, you can leave out the mark: (capture-dynamic-environment) The dynamic environment basically consists of a list of dynamic-wind thunks that can be reestablished with the with-dynamic-environment macro below. + Finally, a with-dynamic-environment macro that allows reestablishing a dynamic environment. You can use with-dynamic-environment as follows: (with-dynamic-environment (environment) ...) It calls all the dynamic-wind thunks in environment (leaving out the proceed parts), and then continues execution with the body of with- dynamic-environment. If you want to have support for dynamic-wind in a call/cc library, you can use with-dynamic-mark whenever you start a call/cc context, use dynamic-wind everywhere, use capture-dynamic-environment in conjunction with call/cc, and use with-dynamic-environment when you invoke a continuation (or put it in the continuation yielded by call/ cc so that this happens automatically). I will furthermore make sure that all dynamically scoped binding forms in ContextL (like with-active-layers, with-inactive-layers, etc.) are re-implemented using dynamic-wind. Any comments on this design? Are there any holes you can notice that I might have missed? Most of the functionality is provided as macros - do you think that's ok, or should I rather go for functional versions? (I would strongly prefer dynamic-wind as a macro, to have better optimization opportunities, but for the other operators, I am willing to negotiate. ;) Please let me know what you think. I'm not online for the next few days, but will hopefully have time again next week to implement this... Best, Pascal P.S.: I now know what bothered me in Scheme's dynamic-wind / call/cc approach: Dynamic-wind is a feature to support dynamic environments, whereas call/cc is a feature to support continuations. The combination overloads one construct with two different semantic concepts. It's true that most of the time you want both combined, but the combination seems forced to me. Dynamic-wind should be part of a library for supporting dynamic scoping, and call/cc should be left untouched. This should also resolve the controversy around dynamic-wind in the Scheme community. Not that I really care that much... ;-) [1] For a better explanation of this, see the excellent paper about Delimited Dynamic Binding at http://okmij.org/ftp/Computation/dynamic-binding.html#DDBinding - especially the sections 4.2.1 to 4.2.3. [2] So Attila was right. ;) -- 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 tcr at freebits.de Sat Dec 27 23:55:53 2008 From: tcr at freebits.de (Tobias C. Rittweiler) Date: Sun, 28 Dec 2008 00:55:53 +0100 Subject: [closer-devel] Adding dynamic-wind to ContextL References: Message-ID: <87lju1b2ba.fsf@freebits.de> Pascal Costanza writes: > (dynamic-wind > (let ((*current-context* (cons something *current-context*))) > (proceed > some code))) > > The code that is captured by dynamic-wind will just be executed as > normal, but whenever a dynamic environment is reestablished that > captures this dynamic-wind, the code here is re-executed up until the > proceed form, which is then replaced with the continuation of > reestablishing that dynamic environment. Proceed is like a progn, > except that it marks the variable part of a dynamic-wind. I haven't fully wrapped my head around all this, but if it makes sense to nest DYNAMIC-WIND, I think DYNAMIC-WIND + PROCEED should be lexically tagged like BLOCK + RETURN-FROM are. -T. From pc at p-cos.net Mon Dec 29 11:33:29 2008 From: pc at p-cos.net (Pascal Costanza) Date: Mon, 29 Dec 2008 12:33:29 +0100 Subject: [closer-devel] Adding dynamic-wind to ContextL In-Reply-To: <87lju1b2ba.fsf@freebits.de> References: <87lju1b2ba.fsf@freebits.de> Message-ID: <7CEB28AF-8A49-4A0C-8673-122B1222BE81@p-cos.net> On 28 Dec 2008, at 00:55, Tobias C. Rittweiler wrote: > Pascal Costanza writes: > >> (dynamic-wind >> (let ((*current-context* (cons something *current-context*))) >> (proceed >> some code))) >> >> The code that is captured by dynamic-wind will just be executed as >> normal, but whenever a dynamic environment is reestablished that >> captures this dynamic-wind, the code here is re-executed up until the >> proceed form, which is then replaced with the continuation of >> reestablishing that dynamic environment. Proceed is like a progn, >> except that it marks the variable part of a dynamic-wind. > > I haven't fully wrapped my head around all this, but if it makes sense > to nest DYNAMIC-WIND, I think DYNAMIC-WIND + PROCEED should be > lexically > tagged like BLOCK + RETURN-FROM are. OK, good point. I don't think this will actually never be needed, but it's no big deal implementing this either, so better be safe than sorry. BTW, does anyone know what the most widely used continuation frameworks for Common Lisp are? I am aware of cl-cont and UCW. Anything else? I would like to ask in their mailing lists what they think of the design and whether they think it's sufficient to combine it with their frameworks... 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 Mon Dec 29 17:49:01 2008 From: drewc at tech.coop (Drew Crampsie) Date: Mon, 29 Dec 2008 09:49:01 -0800 Subject: [closer-devel] Adding dynamic-wind to ContextL In-Reply-To: <7CEB28AF-8A49-4A0C-8673-122B1222BE81@p-cos.net> References: <87lju1b2ba.fsf@freebits.de> <7CEB28AF-8A49-4A0C-8673-122B1222BE81@p-cos.net> Message-ID: Hey Pascal, > > I would like to ask in their mailing lists what they think of the > design and whether they think it's sufficient to combine it with their > frameworks... I am the UCW maintainer now, and as far as i can tell, this design s exactly what i need to fully integrate contextL. I really like the looks of this! Cheers, drewc > > > 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 > > > > > > > > > > _______________________________________________ > closer-devel mailing list > closer-devel at common-lisp.net > http://common-lisp.net/cgi-bin/mailman/listinfo/closer-devel >