Unclear on an answer example.

19 views
Skip to first unread message

Robin Lee Powell

unread,
Oct 6, 2008, 9:20:12 PM10/6/08
to Weblocks

In http://www.defmacro.org/ramblings/continuations-web.html it says:

When the login widget decides if the user can be authenticated
it must call answer with the result - a function that will
restore the saved continuation.

OK, but answer requires a continuation object or "the callee
widget", neither of which appear to be passed to do-page or the
login widget. So how would one actually *use* answer in that
example?

-Robin

--
Lojban Reason #17: http://en.wikipedia.org/wiki/Buffalo_buffalo
Proud Supporter of the Singularity Institute - http://singinst.org/
http://www.digitalkingdom.org/~rlpowell/ *** http://www.lojban.org/

Ian Eslick

unread,
Oct 6, 2008, 11:16:44 PM10/6/08
to webl...@googlegroups.com
One of the more confusing aspects of widget continuation flows, at
least for my humble self, was the linguistic conflation of
continuation function as continuation and widget as continuation.
Here is an attempted blow-by-blow account of the basic call-answer
protocol.

The do-page or do-widget function 'pass control' to a new widget.
Passing control using do-widget (or do-page) works by replacing the
old widget in the hierarchy, or in the case of do-page, the (root-
composite). The hierarchy determines what is seen by the screen which
is everything renderable from the (root-composite). (my apologies if
this is obvious).

Here's the process:

1) Call do-page with a widget we'll creatively name 'new-widget'.
2) do-page captures the current top-level widgets, the composite-
widgets slot of (root-composite), in a closure (see below for what
this does when called).
3) the closure is written in to the continuation slot of new-widget
4) new-widget replaces the old value of (root-composite)

Operations on the hierarchy have to take place inside an action
handler, not as part of the rendering process. When the user invokes
an action on do-widget via a link or form button, the widget would
call answer on itself! This is saying 'answer to my current
continuation' which:

1) funcalls the function stored in the continuation slot of the widget
passed to it, in this case new-widget.
2) the closure body simply reverses what do-page did by replacing the
value of composite-widgets in the (root-composite)

For widgets, this essentially the same as: (funcall (widget-
continuation new-widget) result). if widget-continuation is nil,
however, answer will search up the parent tree for a valid continuation.

Of course this glosses over one detail which is that the state of the
computation is not a simple closure. However, it acts just like what I
said above. If you want to do something more complex than a simple
stack-like call/answer protocol, you're going to need get your head
wrapped completely around call-answer.lisp and dialog.lisp.

Enjoy!
Ian

Robin Lee Powell

unread,
Oct 6, 2008, 11:23:11 PM10/6/08
to webl...@googlegroups.com
On Mon, Oct 06, 2008 at 11:16:44PM -0400, Ian Eslick wrote:
>
> Here's the process:
>
> 1) Call do-page with a widget we'll creatively name 'new-widget'.
>
> 2) do-page captures the current top-level widgets, the composite-
> widgets slot of (root-composite), in a closure (see below for what
> this does when called).
>
> 3) the closure is written in to the continuation slot of
> new-widget
>
> 4) new-widget replaces the old value of (root-composite)
>
> Operations on the hierarchy have to take place inside an action
> handler, not as part of the rendering process. When the user
> invokes an action on do-widget via a link or form button, the
> widget would call answer on itself!

I apologize if this a retarded, but...

How does the widget get a reference to itself?

In the example, (do-page (make-instance 'my-login-widget)) is the
entire do-page call; how does anything inside that get a reference
to the widget created by (make-instance 'my-login-widget) ?

Ian Eslick

unread,
Oct 6, 2008, 11:52:46 PM10/6/08
to webl...@googlegroups.com
I may not understand the question...but here's a simplified version
that should do something along the same lines and illustrates how the
references are passed around.

(defparameter *top* <some widget>)

(defwidget foo () ())

(defmethod render-widget-body (widget &rest args)
(render-link (lambda (&rest args)
(answer widget))))
"This action answers"))

(defun do-page (widget)
(let ((my-top *top*))
(labels ((interior ()
(setf *top* my-top)))
(setf (widget-continuation widget) #'interior)
(setf *top* widget))))

(do-page (make-instance 'foo))

render-widget-body renders an action, which is a closure over the
widget that rendered it. When you click on 'this action answers',
answer is called on the rendering widget (the one that was passed to
do-page). When you call answer, it calls interior, which simply
replaces *top* with the prior value.

It is of course more complex, but the essence of the references is
that the prior state of the tree is stored in the current widget's
continuation, and that the prior state is restored by calling answer
on the new widget or one of it's children.

This help?

Ian

Stephen Compall

unread,
Oct 7, 2008, 1:23:28 AM10/7/08
to webl...@googlegroups.com
Robin Lee Powell <rlpowell-VkGw2YyLwTCa/A/roGhosh2...@public.gmane.org> writes:
> How does the widget get a reference to itself?
>
> In the example, (do-page (make-instance 'my-login-widget)) is the
> entire do-page call; how does anything inside that get a reference
> to the widget created by (make-instance 'my-login-widget) ?

Your actions are usually handled either by methods that receive the
widget as an argument, or closures that receive the widget as an
argument, or closures created in methods that receive the widget as an
argument.

For example, here's a variation on a gridedit subclass that invokes its
own continuation when the user clicks "Close":

(defmethod initialize-instance :after
((self my-gridedit-subclass) &key (allow-close? t) &allow-other-keys)
"Append the Close operation to `answer' from myself if :allow-close?
true, the default."
(when allow-close?
(setf #0=(weblocks:dataseq-common-ops self)
`(,@#0# (close . weblocks:answer)))))

In this case, the handler (which is just answer directly) gets a
reference to the instance of my-gridedit-subclass by virtue of the
advertised dataseq-common-ops protocol, and the implementation of said
protocol gets the instance as the first argument to
dataseq-operations-action.

--
I write stuff at http://failex.blogspot.com/ now. But the post
formatter and themes are terrible for sharing code, the primary
content, so it might go away sooner or later.

Robin Lee Powell

unread,
Oct 7, 2008, 12:47:47 PM10/7/08
to webl...@googlegroups.com
On Mon, Oct 06, 2008 at 11:52:46PM -0400, Ian Eslick wrote:
>
> I may not understand the question...

The question is this: what other code do I need to make the "User
Actions And Continuations" example in
http://www.defmacro.org/ramblings/continuations-web.html actually
*work*?

> but here's a simplified version
> that should do something along the same lines and illustrates how the
> references are passed around.
>
> (defparameter *top* <some widget>)
>
> (defwidget foo () ())
>
> (defmethod render-widget-body (widget &rest args)
> (render-link (lambda (&rest args)
> (answer widget))))
> "This action answers"))
>
> (defun do-page (widget)
> (let ((my-top *top*))
> (labels ((interior ()
> (setf *top* my-top)))
> (setf (widget-continuation widget) #'interior)
> (setf *top* widget))))

OK, you lost me. Why are you defining your own do-page? It's a
weblocks function. Not only that, but weblocks defines it with
defun/cc, which you're not using at all.

> (do-page (make-instance 'foo))
>
> render-widget-body renders an action, which is a closure over the
> widget that rendered it. When you click on 'this action answers',
> answer is called on the rendering widget (the one that was passed
> to do-page). When you call answer, it calls interior, which
> simply replaces *top* with the prior value.
>
> It is of course more complex, but the essence of the references is
> that the prior state of the tree is stored in the current widget's
> continuation, and that the prior state is restored by calling
> answer on the new widget or one of it's children.
>
> This help?

It helps me understand the underlying structure, and I think I
follow how your code works, but it's *far* more complex than the
example at "UserActions And Continuations" in
http://www.defmacro.org/ramblings/continuations-web.html , which
claims that this is all trivial to use. From what that page says, I
shouldn't *need* to fill widget-continuation myself, or even
understand how it gets filled or why.

Robin Lee Powell

unread,
Oct 7, 2008, 12:59:11 PM10/7/08
to webl...@googlegroups.com
On Tue, Oct 07, 2008 at 12:23:28AM -0500, Stephen Compall wrote:
>
> Robin Lee Powell
> <rlpowell-VkGw2YyLwTCa/A/roGhosh2...@public.gmane.org>
> writes:
> > How does the widget get a reference to itself?
> >
> > In the example, (do-page (make-instance 'my-login-widget)) is
> > the entire do-page call; how does anything inside that get a
> > reference to the widget created by (make-instance
> > 'my-login-widget) ?
>
> Your actions are usually handled either by methods that receive
> the widget as an argument, or closures that receive the widget as
> an argument, or closures created in methods that receive the
> widget as an argument.

Until I got farther into the mail, I didn't understand what you
meant by "methods that receive the widget as an argument". I think
I get it now.

> For example, here's a variation on a gridedit subclass that
> invokes its own continuation when the user clicks "Close":
>
> (defmethod initialize-instance :after
> ((self my-gridedit-subclass) &key (allow-close? t) &allow-other-keys)
> "Append the Close operation to `answer' from myself if :allow-close?
> true, the default."
> (when allow-close?
> (setf #0=(weblocks:dataseq-common-ops self)
> `(,@#0# (close . weblocks:answer)))))

*blink*

I have no idea what #0= or @#0# mean. I don't think googling for
them will help, either. :)

> In this case, the handler (which is just answer directly) gets a
> reference to the instance of my-gridedit-subclass by virtue of the
> advertised dataseq-common-ops protocol, and the implementation of
> said protocol gets the instance as the first argument to
> dataseq-operations-action.

Ignoring my total confusion over the formatting, I understand that
the second argument of the common-ops gets passed the widget object,
that it can use to find the continuation, and a cons cell that it's
basically going to ignore. Ah, I see; and access to the widget is
provided by the defmethod argument.

OK. I was assuming that I could call answer inside something simple
like a composite widget, without having to make my own. That
doesn't seem to actually be the case, because unlike with dataseq,
there's no easy way to get at the widget from inside a generic
composite widget (although I suppose I could do something with
widget-suffix-fn).

Anyways, I think I get it now. Thanks!

Robin Lee Powell

unread,
Oct 7, 2008, 1:03:53 PM10/7/08
to webl...@googlegroups.com
Answering the same mail twice, on account of having better
understanding the second time.

On Mon, Oct 06, 2008 at 11:52:46PM -0400, Ian Eslick wrote:
>

> I may not understand the question...but here's a simplified version
> that should do something along the same lines and illustrates how the
> references are passed around.
>
> (defparameter *top* <some widget>)
>
> (defwidget foo () ())
>
> (defmethod render-widget-body (widget &rest args)
> (render-link (lambda (&rest args)
> (answer widget))))
> "This action answers"))

As I said in my answer to Stephen, this is the part I was missing.
I thought it was possible to use answer in a generic widget, such as
composite, without making my own widget at all. I now see that I
can do such a thing only if the widget has a way to run
methods/functions that take the widget as an argument, like the
login widget or dataseq.

> (defun do-page (widget)
> (let ((my-top *top*))
> (labels ((interior ()
> (setf *top* my-top)))
> (setf (widget-continuation widget) #'interior)
> (setf *top* widget))))
>
> (do-page (make-instance 'foo))

And this was just to illustrate the sort of thing that Weblocks does
internally to set the continuations up, correct?

> This help?

Yes, thank you.

Stephen Compall

unread,
Oct 7, 2008, 4:09:30 PM10/7/08
to webl...@googlegroups.com
Robin Lee Powell <rlpowell-VkGw2YyLwTCa/A/roGhosh2...@public.gmane.org> writes:
> As I said in my answer to Stephen, this is the part I was missing.
> I thought it was possible to use answer in a generic widget, such as
> composite, without making my own widget at all. I now see that I
> can do such a thing only if the widget has a way to run
> methods/functions that take the widget as an argument, like the
> login widget or dataseq.

There are other ways to get at a widget. For one, `widget-parent' lets
you walk up the tree. `answer' will also do this for you. For another
(see f-underscore for the weird f things):

(defun init-user-session (root)
(push (let (mymain)
(setf mymain (make-instance 'composite
:widgets (make-my-subwijs (f0 mymain)))))
(composite-widgets root)))

(defun make-my-subwijs (get-container)
(list (make-instance 'foo)
(make-instance 'bar
:on-close (f_% (answer (funcall get-container))))
(make-instance 'baz)))

Here we just used a closure to save the location. However, it would
likely work just as well to pass #'answer to on-close as in this case
the `bar''s parent is the answerable in question.

> I have no idea what #0= or @#0# mean. I don't think googling for them
> will help, either. :)

See CLHS 2.4.8.15 and 2.4.8.16. The @ is separate.

Robin Lee Powell

unread,
Oct 7, 2008, 7:22:20 PM10/7/08
to webl...@googlegroups.com
On Tue, Oct 07, 2008 at 03:09:30PM -0500, Stephen Compall wrote:
>
> Robin Lee Powell
> <rlpowell-VkGw2YyLwTCa/A/roGhosh2...@public.gmane.org>
> writes:
> > As I said in my answer to Stephen, this is the part I was
> > missing. I thought it was possible to use answer in a generic
> > widget, such as composite, without making my own widget at all.
> > I now see that I can do such a thing only if the widget has a
> > way to run methods/functions that take the widget as an
> > argument, like the login widget or dataseq.
>
> There are other ways to get at a widget. For one, `widget-parent'
> lets you walk up the tree. `answer' will also do this for you.
> For another (see f-underscore for the weird f things):

Huh. Looked at the docs for f-underscore; what does (lambda (_)
...) do in CL?

> (defun init-user-session (root)
> (push (let (mymain)
> (setf mymain (make-instance 'composite
> :widgets (make-my-subwijs (f0 mymain)))))
> (composite-widgets root)))
>
> (defun make-my-subwijs (get-container)
> (list (make-instance 'foo)
> (make-instance 'bar
> :on-close (f_% (answer (funcall get-container))))
> (make-instance 'baz)))
>
> Here we just used a closure to save the location. However, it
> would likely work just as well to pass #'answer to on-close as in
> this case the `bar''s parent is the answerable in question.

Neat stuff. Thanks!

Stephen Compall

unread,
Oct 8, 2008, 3:56:10 AM10/8/08
to webl...@googlegroups.com
Robin Lee Powell <rlpowell-VkGw2YyLwTCa/A/roGhosh2...@public.gmane.org> writes:
> Huh. Looked at the docs for f-underscore; what does (lambda (_)
> ...) do in CL?

Mostly it makes a bunch of extra characters that obscure the underlying
meaning of the expression and are therefore unpleasant to type.

Robin Lee Powell

unread,
Oct 9, 2008, 3:06:05 AM10/9/08
to Weblocks
On Mon, Oct 06, 2008 at 06:20:12PM -0700, Robin Lee Powell wrote:
>
>
> In http://www.defmacro.org/ramblings/continuations-web.html it
> says:
>
> When the login widget decides if the user can be authenticated
> it must call answer with the result - a function that will
> restore the saved continuation.
>
> OK, but answer requires a continuation object or "the callee
> widget", neither of which appear to be passed to do-page or the
> login widget. So how would one actually *use* answer in that
> example?

The lovely thread that this spawned got distilled into
http://teddyb.org/rlp/tiki-index.php?page=Learning+About+Weblocks#Using_do_page_do_widget_and_answer
, in case anyone wants to look.

Reply all
Reply to author
Forward
0 new messages