Subject: Re: Vancouver Lisp Users Group meeting for August - Web Continuations Considered Harmful
From: rpw3@rpw3.org (Rob Warnock)
Date: Wed, 09 Aug 2006 22:54:08 -0500
Newsgroups: comp.lang.lisp,comp.lang.scheme
Message-ID: <j4GdnazAuoV9MEfZnZ2dnUVZ_t2dnZ2d@speakeasy.net>
Ken Tilton  <kentilton@gmail.com> wrote:
+---------------
| >> And too bad, Lisp could have used a good web framework.
+---------------

Strange. I hacked together one that was quite adequate [for the
needs of the sites I had to support] in a very short amount of time,
and I wasn't even an experienced CL programmer at the time [though
I did have several years of Scheme usage, including for web stuff].

+---------------
| Ray Dillinger wrote:
| > Ehh, tomayto, tomawto.  Continuation-based web programming is
| > a technique.  It works.  There's really no problem with choosing
| > it over some other techniques, nor with choosing other techniques
| > over it.
| 
| You read my mind. Today I was thinking it does not bother me that anyone 
| wants to use continuations, what bothers me is that that arguable choice 
| (to be debated soon at Lispvan) led to some poor soul deciding to write 
| C instead of Lisp. (To get to the stack so he could do continuations in 
| his homebrewed language.)
+---------------

As I discussed at ILC'03 with Christian Quennec (who mostly agreed),
it's not "real" continuations *per se* that's important, it's the
*idea* of web continuations that's important. Once you've grokked
that idea, you can still program in CL [or anything else] by simply
reifying externalizable markers for your "continuations", and then
dispatching on them when they come back in.

In fact, "real" continuations *are* somewhat harmful, since they
imply a *single* place a.k.a. flow-of-control to re-enter when the
user clicks "Submit". Often one needs pages with several *different*
HTML forms in them, each with a different "continuation" to evoke.
If you use "real" continuations you are then forced to add an
explicit dispatch layer... but if you do *that*, you might as
well be using the simple "pseudo-continuations" I use, which are
just tokens [stored in hidden form variables] containing the
"Where do I want to go now?" information.

Said another way, my simple web framework contains the following:

;;;
;;; Web Continuations Module (well, the feeble beginnings of one).
;;;

;;; Within an HTML <form>, (BUILD-CONTINUATION :var1 "val1" :var2 "val2")
;;; will pass "var1=val1&var2=val2" in the CGI bindings.
;;; Includes automatic debug-state propagation.
(defun build-continuation (&rest rest &key (stream *standard-output*)
                                      &allow-other-keys)
  (with-html-output (s stream t)
    (loop for (name value) on rest by #'cddr
          and first = t then first do
      (unless (eq name :stream)
        (when (and first *debug*)
          (setf first nil)
          (htm "[debug = " *debug* ", k = {" name "," value "}]" :br (lfd)
               (:input (:type "hidden" :name "debug" :value "yes")) (lfd)))
        (htm (:input (:type "hidden" :name name :value value)) (lfd))))))

and a typical usage might be like so [the :STATE is the critical bit]:

    (defun search-type2-page (stream self results bindings)
      (with-html-output (s stream t)
	(:form (:method :post :action self)
	  (build-continuation :stream s :state "type2-results"
			      :m_ci "yes" :m_pm "no")
	  (:b () "1. Select how much to display:") :br (lfd)
	  (:table (:border 0 :cellpadding 0)
	    (:tr () (:td ()
              (:input (:type "radio" :name "m_sel" :value "min"
                 :checked nil))
              "Name &amp; email only." (lfd)
              (:input (:type "radio" :name "m_sel" :value "mid"))
              "Name, email, &amp; phones." (lfd)
              (:input (:type "radio" :name "m_sel" :value "max"))
              "All columns." (lfd))))
	  ...The rest of the page...)))

It's been "continuations" enough for me...  ;-}  ;-}


-Rob

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607