Subject: Re: Lisp backend protocol
From: rpw3@rpw3.org (Rob Warnock)
Date: Sat, 01 Apr 2006 22:31:38 -0600
Newsgroups: comp.lang.lisp
Message-ID: <P_ydnY74Nr03zrLZRVn-tQ@speakeasy.net>
Ari Johnson  <ari@theari.com> wrote:
+---------------
|  rpw3@rpw3.org (Rob Warnock) wrote:
| > Congratulations: You've just re-invented IPC and/or RPC.
| 
| I haven't reinvented IPC.  I've suggested a layer on top of it for the 
| express purpose of making Lisp backend logic available to other-language 
| frontends.  Does anything already exist for *that*?
+---------------

It certainly *exists*, though probably not as a preconfigured
open-source package one can just slurp off the 'Net. For myself,
when I was starting to use CL seriously and was worried about
"startup time" I actually used "mod_lisp" protocol for some of
that sort of thing, since I always have a persistent Lisp-based
web server running anyway. A simple shell script wrapped around
a "sock.cgi" C program [that connects to a Unix-domain socket
and speaks "mod_lisp" protocol to it] was enough to fake up a
web request, and the answers came out of the standard-output of
the C program, so you could write script that looked like this:

    #!/bin/sh
    env HTTP_HOST=localhost \
	REQUEST_METHOD=MAGIC_LOCAL_GET \
	MAGIC_LOCAL_AUTH=cGFzc3dvcmQ9ZnJlZWJsZXNuYXR6 \
	REQUEST_URI=/ipc/run_function/foo \ | sock.cgi | whatever...

But then I found out that simple "scripting" with CMUCL is plenty
fast for me, so I stopped doing the above:

    $ cat test.lisp
    #!/usr/local/bin/cmucl -script
    (format t "hello world!~%")
    $ ./test.lisp
    hello world!
    $ time-hist ./test.lisp
    Timing 100 runs of: ./test.lisp
      54 0.016
      46 0.017
    1.156u 1.283s 0:02.88 84.3%     264+1700k 1+0io 2pf+0w
    $ 

Since 16ms is way faster than *my* reaction time, I figure it's
"fast enough"...

And if you're talking about larger programs that might want to
use a bunch of libraries, well, just save a Lisp image with all
that stuff in it already and your own startup function, and you
can run that from a script pretty quickly, too. Here's an example
using CMUCL [the "bin/" and "lib/" subdirectories are so CMUCL
can find its core file without an explicit "-core" in the script
"#!" line, since some operating systems restrict that to a single
additional argument]:

    $ mkdir -p ~/bin/cmu-images/foo/{bin,lib}
    $ ln /abs/path/to/standard/cmucl ~/bin/cmu-images/foo/bin
    $ ~/bin/cmu-images/foo/bin/lisp
    ...[lots of chatter]...
    * (asdf :cl-ppcre)
    ; loading system definition from library:local/systems/cl-ppcre.asd into
    ; #<The ASDF1497 package>
    ; Loading #p"/u/lisp/contrib/cl-ppcre-0.7.6/cl-ppcre.asd".
    ...[lots more chatter]...
    * ...[whatever else you want to load]...
    ...[lots more chatter]...
    * (defun main ()
	(format t "MAIN called:~%")
	(format t "*COMMAND-LINE-STRINGS* = ~s~%" ext:*command-line-strings*)
	(let ((script (cadr ext:*command-line-strings*)))
	  (when script
	    (format t "Script contents:~%")
	    (with-open-file (s script)
	      (loop for line = (read-line s nil nil)
		    and line-number from 1
		    while line do
		(format t "Line ~d:~8t~s~%" line-number line)))))
	(unix:unix-exit 0))

    MAIN
    *  (save-lisp "/u/rpw3/bin/cmu-images/foo/lib/lisp.core"
		  :init-function #'main
		  :load-init-file nil
		  :site-init nil
		  :print-herald nil
		  :batch-mode t)
    [Doing purification: Done.]
    [Undoing binding stack... done]
    [Saving current lisp image into /u/rpw3/bin/cmu-images/foo/lib/lisp.core:
    Writing 20608624 bytes from the Read-Only space at 0x10000000.
    Writing 3198464 bytes from the Static space at 0x28F00000.
    Writing 4096 bytes from the Dynamic space at 0x48000000.
    done.]

    $ cat >~/bin/foo
    #!/u/rpw3/bin/cmu-images/foo/bin/lisp
    First line of script
    Second line of script
    Last line of script
    ^D
    $ chmod +x ~/bin/foo
    $ foo bar baz
    MAIN called:
    *COMMAND-LINE-STRINGS* = ("/u/rpw3/bin/cmu-images/foo/bin/lisp"
			      "/u/rpw3/bin/foo" "bar" "baz")
    Script contents:
    Line 1: "#!/u/rpw3/bin/cmu-images/foo/bin/lisp"
    Line 2: "First line of script"
    Line 3: "Second line of script"
    Line 4: "Last line of script"
    $ time-hist foo bar baz
    Timing 100 runs of: foo bar baz
      38 0.016
      34 0.017
      28 0.018
    1.283u 1.218s 0:02.55 97.6%     263+1726k 0+0io 0pf+0w
    $

Note: I'm not saying having a persistent Lisp server running
to service calls from other languages is a *bad* thing -- it
indeed can be useful under some circumstances [although designing
the security protocols can be "interesting"]. But there are
much better ways of addressing the "startup time" boogieman.


-Rob

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