We jumped into an adventure to build the simplest way possible to
deploy a Scheme web application. In the previous article, we identified
two procedures, petit-cloud-http-connect
, and the kernel
procedure make-petit-cloud-application
. We will dive into
the latter.
Any resemblance to competitor technology is wholly, and fully coincidental.
We want to deploy http web application built with a library
(hello)
with a procedure main
:
;; hello.scm
(library (hello)
(export main)
(import (petit-cloud application))
define main
(lambda (method path headers body)
(values 200
(list (cons 'content-type "text/plain"))
(string->utf8 "Hello schemer!\n"))))) (
Given an application server running at DOMAIN:PORT
, it
must be possible to deploy it with the following shell invokation:
#;sh> petit-cloud deploy DOMAIN:PORT hello.scm
Short of logging, monitoring devices, and error handling, the application server code is:
(import (petit-cloud))
define petit-cloud-deploy?
(lambda (method path)
(...))
define client-accept
(lambda (read write close)
(read))
(define-values (method url headers body) (if (petit-cloud-deploy? method path)
(let ((application (make-petit-cloud-application body)))
(
(petit-cloud-current-application application)values 201 (list) (bytevector)))
(let ((application (petit-cloud-current-application)))
(
(application method url headers body)))))
(petit-cloud-http-connect DOMAIN PORT client-accept)
The procedure make-petit-cloud-application
takes a
bytevector as argument that is read into a symbolic expression that is
the same as the library (hello)
:
;; hello.scm
(library (hello)
(export main)
(import (petit-cloud application))
define main
(lambda (method path headers body)
(values 200
(list (cons 'content-type "text/plain"))
(string->utf8 "Hello schemer!\n"))))) (
We want to translate the library (hello)
into executable
code, in other words convert data into code.
Turning data, into a program can be done with an interpreter.
In the case of Scheme, is to use the readily available
eval
.1
Even if they are ways to make it safer, or safe, the procedure
eval
is unsafe, it can have side effects, and other effects that will have significant security consequences: do not expose such an application server in open Internet without the careful review of an expert.
As of 2023, R7RS specify eval
as follow:
eval expr-or-definition environment-specifier) (
The form library
is neither an expression, or definition
it is in most Scheme implementation a meta-syntaxic device hardcoded in
the macro expander, the same component responsible for interpreting
macros.
We could rename, and write (hello)
on disk, but there is
a more straightforward way, a transformation relying on
define
, set!
, and let
. The
library (hello)
can be rewritten into an expression that
can be handed to eval
to return the procedure
main
.
The transformation will yield the following expression called
module
:
begin
(define main #f)
(
let ()
(
set! main
(lambda (method path headers body)
(values 200
(list (cons 'content-type "text/plain"))
(string->utf8 "Hello schemer!\n")))))
(
main)
Then, the following snippet, will return main
:
eval module '(petit-cloud application)) (
That is enough to known to implement a simple
make-petit-cloud-application
.
We described how to transform an library definition, into an
expression that can be fed into eval
, that will return the
procedure that will eventually reply to end-users.
That is not a complete implementation, more code are necessary to make this outline fit for production. If you want to use such an application server, and self-host it, the code is within reach.
home.