Common Lisp Scripting with Roswell
"Roswell Script" is implementation-independent Common Lisp scripting program which uses Roswell. Although Roswell itself is a unified interface to Common Lisp implementations, it also encourages writing scripts with it.
To start writing it, run
ros init in your terminal:
$ ros init Usage: ros init [template] name [options...] $ ros init fact Successfully generated: fact.ros
It adds a file extension
Perhaps it's hardly understandable if you have been using only Unix OSes for years like me. Actually, it works well even without
.ros on Unix, so renaming it to the name which doesn't have
.ros is a usual manner if the script doesn't have to run on Windows. However, it helps Roswell to differentiate it from shell scripts, and Roswell provides some additional features for it like Making Executables.
The content of the file looks like this:
#!/bin/sh #|-*- mode:lisp -*-|# #| exec ros -Q -- $0 "$@" |# (defun main (&rest argv) (declare (ignorable argv)))
This looks a little bit hacky. It actually is a shell script which
exec Roswell immediately. Roswell runs the same script, skips multi-line comments, reads the rest of the file as a Common Lisp program, and finally invokes a function
main with command-line arguments.
Here's an example program which takes the exact one argument and prints its factorial:
#!/bin/sh #|-*- mode:lisp -*-|# #| exec ros -Q -- $0 "$@" |# (defun fact (n) (if (zerop n) 1 (* n (fact (1- n))))) (defun main (n &rest argv) (declare (ignore argv)) (format t "~&Factorial ~D = ~D~%" n (fact (parse-integer n))))
$ fact.ros 3 Factorial 3 = 6 $ fact.ros 10 Factorial 10 = 3628800
Though it falls through CL debugger if there's no arguments or non-integer value, I assume it's okay for now as a simple example ;)
Hey! It's a script. And Common Lisp is pretty fast programming language like C, right?
Well, unfortunately, when scripting in Common Lisp, you have to face its startup time.
$ time fact.ros 10 Factorial 10 = 3628800 fact.ros 10 0.74s user 0.21s system 95% cpu 0.994 total
This very simple program took 0.74s to print the answer. Considering that all Common Lispers can easily compute the factorial of 10 in less than 1 sec, it runs unbearably slow to help us anyway.
One easy solution is skipping loading Quicklisp, as it doesn't require it.
-Q flag by
+Q in the
#!/bin/sh #|-*- mode:lisp -*-|# #| exec ros +Q -- $0 "$@" |# ...
time command to see how it worked:
$ time fact.ros 10 Factorial 10 = 3628800 fact.ros 10 0.57s user 0.19s system 97% cpu 0.780 total
It's a little bit better.
Roswell also provides a command to convert a script into an executable,
$ ros build fact.ros [undoing binding stack and other enclosing state... done] [saving current Lisp image into fact: writing 4976 bytes from the read-only space at 0x20000000 writing 3168 bytes from the static space at 0x20100000 writing 52330496 bytes from the dynamic space at 0x1000000000 done]
An executable version is generated at
fact. If your script doesn't have a file extension
.ros, Roswell overrides your file and the behaviour is unknown.
time for the executable:
$ time fact 10 Factorial 10 = 3628800 fact 10 0.00s user 0.02s system 96% cpu 0.024 total
Well, is it fast enough now? :)
Distributing Scripts via Quicklisp
When you write a script which would make other people happy, think about distributing it.
The easiest way to do it is, just sending the
.ros file to others. But, it's hard if your script is more like a "library", so too big to put all stuff into a single file.
Or, if you're a library author, you may think of providing a command-line interface to it.
ros install would be a solution for it. It's known as a command for installing Common Lisp implementation, however,it is also a command for installing Common Lisp libraries.
$ ros install <library name>
It downloads the specified library from Quicklisp, and it installs bundled Roswell scripts if there's any.
For instance, try
ros install clack:
$ ros install clack found system clack. Attempting install scripts... /Users/nitro_idiot/.roswell/bin/clackup
It copies script files which located the
roswell/ directory of the library into
~/.roswell/bin and gives an executable flag to them.
Don't forget to add
$PATH to use those scripts:
$ echo "export PATH=\"\$HOME/.roswell/bin:\$PATH\"" >> ~/.zshrc
Here're libraries bundling Roswell scripts.