The Web, Continuations, and All That

2012-01-22 Thread Tobias Gerdin
Hello,

To get better acquainted with continuations I have been playing with
them in the context of web programming a bit.  Much has been written
on the topic (in particular I find the Racket tutorials very
instructive), and here I would just like to share the small
experiments I did using delimited continuations and the Guile (web
server). Kudos to Andy for providing a very nice foundation.

The ingredients:

(use-modules (web server)
 (web request)
 (web response)
 (web uri)
 (sxml simple)
 (ice-9 control))

To begin with, a simple request handler making use of a table to map
between request URI paths and other request handlers (also functions
as a continuation table):

(define dispatch-table (make-hash-table))

(define (handler request body)
  (let* ((path (split-and-decode-uri-path (uri-path (request-uri request
 (h (hash-ref dispatch-table (string->symbol (car path)
(if h
(% (h request body))
(values '((content-type . (text/plain)))
(string-append "Unknown page: " (car path))

So a prompt is installed just before the matching request handler is
dispatched to.

Next there is Christian Queinnec's `show' operator (see Christian
Queinnec's seminal "The Influence of Browsers on Evaluators or,
Continuations to Program Web Servers"), although here I've call it
`send-suspend', which is what it is called in Racket (actually, they
call it `send/suspend'):

(define (send-suspend make-response)
  (abort (lambda (k)
(let ((k-uri-id (gensym "k"))) ; Use something more
sophisticated here!
  (hash-set! dispatch-table k-uri-id k)
  (make-response (format #f "/~a" k-uri-id))

So it's a something that when called will save the state of the
computation (the continuation `k') in the table and associate it with
a generated uri, and then apply that uri to a procedure that returns a
response object. The comment is there to draw attention to the fact
that security hinges on the generated uri being difficult to predict,
or else the user's session is open to hijacking. Additional security
measures could be put in place as well.

The point of the above is to able to write request handlers spanning
several requests in this style:

(define (hello-world r b)
  (send-suspend
   (lambda (k-url)
 (values '((content-type . (text/html)))
 (string-append "hello"
  (values '((content-type . (text/html)))
  "world!"))

To try it the handler needs to be mounted and the web server fired up,
pointed at the dispatching handler:

(hash-set! dispatch-table 'hello hello-world)
(run-server handler)

Visiting http://localhost:8080/hello should give a "hello" link
yielding a second page saying "world!". Since constructing the
response objects in the above way is tedious and verbose I made use of
the `respond' helper from the "Web Examples" section in the Guile
reference manual:

(define (hello-world2 r b)
  (send-suspend
   (lambda (k-uri)
 (respond `((a (@ (href ,k-uri)) "hello")
  (respond '("world!")))

(hash-set! dispatch-table 'hello2 hello-world)

Lexical scope is preserved over requests:

(define (count req body)
  (let loop ((n 0))
(send-suspend
 (lambda (k-uri)
   (respond `((a (@ (href ,k-uri)) ,(number->string n))
(loop (1+ n

(hash-set! dispatch-table 'count count)

An common use-case is to return values from pages (such as data
provided by the user through forms):

;; Dirty hack to extract a single param from encoded form data
(define (get-name form-data)
  (uri-decode (cadr (string-split form-data #\=

(define (get-greet req body)
  (let ((req (send-suspend
  (lambda (k-uri)
(respond `((form (@ (action ,k-uri) (method "GET))
 "Your name: "
 (input (@ (type text) (name name)))
 (input (@ (type submit))
(respond `("Hi " ,(get-name (uri-query (request-uri req)))

(hash-set! dispatch-table 'greet get-greet)

Since the stored continuation is invoked with both the request and the
body you access the latter by making use of some multiple-values
binding form:

(use-modules (srfi srfi-11)); let-values
(use-modules (rnrs bytevectors)); utf8->string

(define (post-greet req body)
  (let-values (((req body) (send-suspend
(lambda (k-uri)
  (respond `((form (@ (action ,k-uri)
(method "POST"))
   "Your name: "
   (input (@ (type text)
(name name)))
   (input (@ (type submit))
(respond `("Hi " ,(get-name (utf8->string body))

(hash-set! dispatch-table 'greet2 post-greet)

Happy hacking.

-Tobias



Re: The Web, Continuations, and All That

2012-01-22 Thread Ian Price
Tobias Gerdin  writes:

> Hello,
>
> To get better acquainted with continuations I have been playing with
> them in the context of web programming a bit.  Much has been written
> on the topic (in particular I find the Racket tutorials very
> instructive), and here I would just like to share the small
> experiments I did using delimited continuations and the Guile (web
> server). Kudos to Andy for providing a very nice foundation.

It's always nice to see someone playing with continuations. Your example
is very similar to one I did a while back https://gist.github.com/1381107.

It would be if someone(nudge nudge) were to take the effort to make one
of these experiments practical, since a guile web framework seems to be
a common request.

-- 
Ian Price

"Programming is like pinball. The reward for doing it well is
the opportunity to do it again" - from "The Wizardy Compiled"



Re: Guile support in GNU make

2012-01-22 Thread Paul Smith
On Sun, 2012-01-22 at 22:29 +0400, Kirill Smelkov wrote:
> Then a bug report: running make check on taday's make from CVS and with
> guile-2.0 from Debian gives this:
> 
> *** work/functions/guile.base Sun Jan 22 22:21:18 2012
> --- work/functions/guile.log  Sun Jan 22 22:21:18 2012
> ***
> *** 6,8 
> --- 6,13 
>   bar
>   a b
>   a b c d 1 2 3
> + 
> + Some deprecated features have been used.  Set the environment
> + variable GUILE_WARN_DEPRECATED to "detailed" and rerun the
> + program to get more information.  Set it to "no" to suppress
> + this message.
> 
> I've tried to set that GUILE_WARN_DEPRECATED to "no", but the warning
> persists to stay. Perhaps it would be a good idea not to use deprecated
> features anyway...

Setting that variable doesn't do anything because the test suite strips
out all but a few "known good" variables.

So, maybe the Guile folks can give me some portability guidance.  The
message Guile gives (when I set the above to "detailed") is this:

Guile used to use the wrong argument order for string-delete.
This call to string-filter had the arguments in the wrong order.
See SRFI-13 for more details. At some point we will remove this hack.

and it's because of this code in gmk-default.scm:

   ;; Printable string (no special characters)
   ((and (string? x)
 (eq? (string-length (string-delete x char-set:printing)) 0))
x)

It's trying to determine if the string contains any non-printable chars.


How can I write this so it will work both with older Guile 1.8 and also
with newer Guile 2.0?  Or, should I just forget about trying to work
with Guile <2.0?  Most systems I have access to still have Guile 1.8
though.

-- 
---
 Paul D. Smith   Find some GNU make tips at:
 http://www.gnu.org  http://make.mad-scientist.net
 "Please remain calm...I may be mad, but I am a professional." --Mad Scientist




Re: Guile support in GNU make

2012-01-22 Thread Thien-Thi Nguyen
() Paul Smith 
() Sun, 22 Jan 2012 16:56:19 -0500

  ;; Printable string (no special characters)
  ((and (string? x)
(eq? (string-length (string-delete x char-set:printing)) 0))
   x)

   It's trying to determine if the string contains any non-printable chars.

   How can I write this [...]

Use ‘and-map’ with a predicate wrapped around ‘char-set:printing’ directly.



Re: Guile support in GNU make

2012-01-22 Thread Thien-Thi Nguyen
() Thien-Thi Nguyen 
() Mon, 23 Jan 2012 07:01:46 +0100

   Use ‘and-map’ with a predicate wrapped around ‘char-set:printing’ directly.

Blech, pre-caffeine posting...

Turns out ‘and-map’ is not documented in the Guile 1.8 Manual.  :-/
Better is ‘string-fold’ (w/ the same predicate), then.
Best is ‘string-every’ w/ ‘char-set:printing’ directly.