cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Daniel Fagerstrom <Daniel.Fagerst...@lentus.se>
Subject SV: continuation fear (was Re: [status & RT] design challenges)
Date Fri, 12 Apr 2002 12:17:52 GMT
ovidiu@apache.org wrote:
<snip/>
> Suppose the application's entry point is the 'startShopping' function,
> which is called from the sitemap using the <map:call
> function="startShopping"/> entry. Our application displays a list of
> items that can be bought as links. When one of the items is clicked,
> we want to add it in the shopping cart, and display the list of items
> again. We do this until the user clicks on the "Checkout" button
> located on the same page.
>
> The page also displays a little shopping cart icon, which when
> clicked, displays the items currently in the cart.
>
> We'd have something like this then:
>
> function ShoppingSite()
> {
>   this.cart = new java.util.HashMap();
> }
>
> function ShoppingSite_chooseItem()
> {
>   while (true) {
>     // Initialize this by calling the business logic
>     var itemsToBeDisplayed = ...;
>
>     sendPage("chooseItems.xml", itemsToBeDisplayed);
>     // Either one item was chosen or a view-cart or checkout happened
>     var item = cocoon.request.getParameter("item");
>
>     // If the "item" parameter is present in the request, the user
>     // chose an item. Otherwise consider one of the other buttons or
>     // links has been clicked, in which case the "operation" parameter is
>     // set. The handleOperation() function deals with this.
>
>     if (item != null) {
>       // We need to add one of the items in the basket. In case the
>       // item is already there, add one more.
>       cart.put(item, 1 + cart.get(item));
>     }
>     else
>       handleOperation(this, k);
>   }
> }
> ShoppingSite.prototype.chooseItem = ShoppingSite_chooseItem;
>
> function ShoppingSite_checkout()
> {
>   // Do the checkout using the "cart" instance variable
> }
> ShoppingSite.prototype.checkout = ShoppingSite_checkout;
>
> function ShoppingSite_showCart()
> {
>   sendPage("showCart.html", this.cart);
>   handleOperation(object);
> }
> ShoppingSite.prototype.showCart = ShoppingSite_showCart;
>
> function handleOperation(object)
> {
>   var operation = cocoon.request.getParameter("operation");
>   if (operation != null
>       && operation in object
>       && object[operation] instanceof Function)
>     object[operation]();
> }
>
> // The entry point function in the shopping site example
> function startShopping()
> {
>   var shopper = new ShoppingSite();
>   shopper.chooseItem();
> }
>
>
> The above shows a JavaScript class ShoppingSite which handles all the
> various operations usually associated with such an activity. The
> startShopping() entry point function in the sitemap creates such an
> instance and invokes on it the chooseItem() method. This will loop
> forever, waiting either for an item to be selected, or for an
> operation to happen. Notice the handleOperation() function which does
> this dispatch. To add more operations, you only need to define one
> more instance method to the ShoppingSite class.
>
> Now to answer your question about going back in the history and
> choosing a different path. Notice that the 'cart' instance variable
> uses a shared Java HashMap instance. This means the same instance is
> available to all the continuations, and any modification to it is
> visible from the other continuations. This means that if you go back
> and modify something in the page #2, the result will affect the cart
> in page #12.
>
> If you want to consider that once the user went back to page #2, s/he
> has in the cart only the item he chose in page #1, you'd have to copy,
> perhaps in a lazy fashion to avoid memory bloat, the 'cart' instance
> variable. This way each page in the tree maintains its own copy of the
> cart.
<snip/>

Thank you for your clarifying answer!

Let, see if I get it right: for traditional webapps like a shopping site you
are updating global variables, shoping cart, adress info and so on. For
"what if" scenarious you use local variables, and creates the possiblity to
"undo" things by creating new stack frames, i.e. by going into a new block
or calling a function.

IIUC, in the shopping site example above we are in effect only using the
application global variable "shopper", the local variables are transient and
could be optimized away from the saved continuations with a clever
implementation. As a result the continuation objects only need to contain a
pointer to the "global" variables and a stack of program counters. The stack
of program counters could furthermore be optimized away I belive, in the
above example by using tail recursion optimization and by "inline expansion"
of some of the functions. In the scenario we also need a mechanism for
preventing (or at least warn) the user from reloading the page that
excecutes <map:call function="startShopping"/> as this would lead to several
independent shopping sessions, which might be highly confusing.

Thinking more about the shopping example I think I would like to inverse the
control between the sitemap and the flowmap: After all, the "shopping
around" part of the story is mainly web-publishing. There is a large
database of products that are to be presented, there is a search engine and
the access patterns are unstructured rather than linear. All this seem like
the tasks where the sitemap excel. Embedded in the pages we might have an
overview of the items in the shopping cart, personalized recomendations,
links to earlier visited product pages and so on, but this  seem still to be
information of global character that would easily be presentable with
publishing oriented concepts, already implemented in Cocoon.

IMO the role for a flowmap is to take care of structured interactions
(wizards) and in the shopping case, examples of such are: checking out,
editing the shopping cart and uppdating your preferences. So, on a product
page there are links like "http://www.foo.com/addToCart.html?prodNo=123456"
that call a flowmap function <map:call function="addToCart"/> and
"http://www.foo.com/checkOut.html" that call <map:call function="
checkOut"/> and so on. These flowmap functions all works on application
global variables, and takes care of structured user interaction.

This far in the discussion one might start to get the herretic thought: Ok,
handling "what if" scenarious is really cool, but is it worth the effort of
saving all the stack frames? Isn't uppdating the old, boring and global
session attributes, when thinking about it, a rather good model of what a
user might expect to experience from e.g. a shopping site?

To conclude: I believe that using continuations is a good idea as it allows
us to use control flow concepts from structured programming in our flowmaps:
sequence, choice and repetition. The question is: how much should we store
in the continuation? If we only use a program pointer we can use basic
control flow together with global variables, storing the call stack (a stack
of program pointers) we can use (possibly recursive) functions, and by
storing all the stack frame, local variables and thus "what if" scenarios
become possible.

Before having seen some "must have" real world use cases for "what if"
scenarios, I tend to think that just storing the call stack would be more
than enough in a flowmap language.

What do you think?

/Daniel Fagerstrom


---------------------------------------------------------------------
To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
For additional commands, email: cocoon-dev-help@xml.apache.org


Mime
View raw message