v
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"))
("Hello schemer!\n"))))) (string->utf8
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"))
("Hello schemer!\n"))))) (string->utf8
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! mainlambda (method path headers body)
(values 200
(list (cons 'content-type "text/plain"))
("Hello schemer!\n")))))
(string->utf8
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.
The procedure eval
can be implemented as an interpreter, or compiler↩︎