
Common Lisp as a Scripting Language, 2015 Edition - wglb
http://fare.livejournal.com/184127.html
======
frou_dh
I have my own scheme-esque language that I use for scripts. 16 of its 48
primitive procedures ~correspond to unix system calls (open, close, read,
write, seek, select, stat, link, unlink, mkdir, lsdir, chdir, fork+exec, wait,
sleep, exit). It was very instructive to work on, and using something I know
inside out is a welcome relief from the uncertainty I feel when intuiting
(ba)sh.

~~~
dorfsmay
Any reason you don't use one of the existing scriptable scheme like guile's?

~~~
frou_dh
Wanted to put my article-gleaned understanding of lisp to the test + get at
the essence of what shebang-style unix scripting was about.

Here is a reimplementation of "cat" in it:

    
    
        #!/usr/bin/env imp
    
        (def copy-out (proc (x)
          (let f (if (file? x) x (open x))
               bytes (read default-chunk f)
               (if bytes
                   (seq (write bytes stdout)
                        (recur f))
                   (close f)))))
    
        (if (nil? script-args)
            (copy-out stdin)
            (each copy-out script-args))
    

Running external programs is a bit more verbose than normal shell, but there
is some optional reader-macro sugar on top of fork+exec, e.g. the effect of
the following is that /usr/bin/say gets run with those non-string args
converted to strings and a voice comes out of my speaker.

    
    
        !(say hello there)
        => ((status : 0) (pid : 2945) (in : [File]) (out : [File]) (err : [File]))

~~~
dorfsmay
> Wanted to put my article-gleaned understanding of lisp to the test

fair enough!

> get at the essence of what shebang-style unix scripting was about

FWIW, although convoluted, you can do that with guile, with:

    
    
        #!/usr/bin/guile \
        -e main -s
    

Or simpler with chicken:

    
    
      #!/usr/bin/csi -s

~~~
rml
IMHO the nicest Scheme for UNIX scripting is scsh. Its APIs are really well
thought out and represent a lot of design work that hasn't ever been
replicated by another Scheme (at least that I've seen).

Some good examples of scsh scripting (some by the author, Olin Shivers) can be
found at

[http://scsh.net/resources/scripts.html](http://scsh.net/resources/scripts.html)

The manual is also very good:

[http://scsh.net/docu/html/man.html](http://scsh.net/docu/html/man.html)

~~~
juanuys
> The manual is also very good

The acknowledgements section made my morning, thanks.

------
TeMPOraL
I'm starting to use SBCL for scripting, and on the system with a Lisp on an
SSD drive the startup delay is unnoticeable. My friends tell me that on non-
SSD setups, adding --script to startup options makes SBCL load almost
instantly. It skips various initializations, disables debugger and REPL, and
ignores shebang if it's the first line of a script, allowing you to write:

#!/usr/bin/sbcl --script

in your .lisp file.

------
mark_l_watson
I used Common Lisp from the mid 1980s until about 5 years ago when my
customers wanted Clojure, if they wanted Lisp development done. I have never
seriously used CL as a scripting language but for a while I was using Gambit-C
Scheme for small compiled apps and command line tools. Gambit-C is very good
for this purpose.

BTW, Clojure is "out" for command line tools because of the JVM startup time
but I have thought about Clojurescript + node.

~~~
whateveracct
Nitpick, but Clojure's startup time isn't JVM startup time it's clojure.core
startup time

~~~
e12e
This appears to be true, eg:

    
    
      cat <<eof > Hello.java
      public class Hello {
        public static void main(String[] args) {
          System.out.println("Hello, world!"); }}
      eof
    
      javac Hello.java
      echo "Main-Class: Hello" > manifest.mf
      jar cvfm hello.jar manifest.mf Hello.class
      time java -jar hello.jar 
      Hello, world!
    
      real    0m0.153s
      user    0m0.132s
      sys     0m0.020s
    

vs:

    
    
      lein new hello
      cd hello
      # Pesty s-expressions, so hard to work with... ;-)
      cat <<eof|patch -p0
      --- project.clj 2015-04-16 04:13:09.734160136 +0200
      +++ project.clj.patch   2015-04-16 04:09:33.809504079 +0200
      @@ -3,4 +3,5 @@
         :url "http://example.com/FIXME"
         :license {:name "Eclipse Public License"
                   :url "http://www.eclipse.org/legal/epl-v10.html"}
      +  :main hello.core
         :dependencies [[org.clojure/clojure "1.6.0"]])
      eof
    
      cat<<eof > src/hello/core.clj
      (ns hello.core
        (:gen-class))
      (defn -main 
        "I don't do a whole lot."
        []
        (println "Hello, World!"))
      eof
    
      lein uberjar
      
      time java -jar target/hello-0.1.0-SNAPSHOT-standalone.jar 

Hello, World!

    
    
      real    0m1.060s
      user    0m1.400s
      sys     0m0.040s
    

(Intel(R) Core(TM) i5-2520M CPU @ 2.50GHz, SSD drive)

~~~
lispm
Lisp on my Raspberry Pi is faster:

    
    
        pi@raspberrypi ~/lisp/ccl $ time ./armcl -n -Q -e '(progn (princ "hello") (quit))'
        hello
    
        real	0m0.112s
        user	0m0.020s
        sys	        0m0.080s
    

And the expression is first compiled to native code...

~~~
e12e
How does guile 2.0 do on that hardware?

~~~
lispm
Don't know. I don't use it.

------
e40
I use the _#!_ trick for scripts. Here's a Common Lisp script that sends me
email every morning (run from cron) about AWS resources being used:

[https://gist.github.com/franzinc/acde9fd04a6b07745db3](https://gist.github.com/franzinc/acde9fd04a6b07745db3)

------
jlarocco
I use SBCL for some scripting, and haven't found the overhead to be that bad
just using #!/usr/bin/sbcl. For basic stuff, I just use zsh, so by the time
I've switched to Lisp, the task is already at the point where startup time
isn't a big factor.

I do have some compiled programs I use, but IMO they're a little too big to be
called scripts.

An interesting idea would be the ability to dump a shared library of the core
image, and then dump incremental changes as executable images that dynamically
link to it.

------
DalekBaldwin
I wonder if the scales would tilt more in favor of separate images if you
added a good tree-shaking step. You can bang out useful scripts pretty fast by
pulling in a few helper functions from large libraries, but the more scripts
share a library, the less functionality you can discard from the final image.

If you can cut the size of the core language's image in half with just a
simple method like this, you could probably get impressive results using more
sophisticated methods on images requiring a lot of libraries:
[https://gist.github.com/burtonsamograd/f08f561264ff94391300](https://gist.github.com/burtonsamograd/f08f561264ff94391300)

------
arto
Fellow Lispers may perhaps also find helpful these notes I put together
recently on shebang lines for various open-source Common Lisp implementations:

[http://ar.to/notes/common-lisp#scripting-with-common-
lisp](http://ar.to/notes/common-lisp#scripting-with-common-lisp)

------
outworlder
I have used Chicken Scheme for a lot of script-like tasks.

If one doesn't want to compile first, that's fine. The interpreter's startup
time is negligible.

------
crististm
"the EliteBook has a touchpad that is particularly bad, even worse than the
Thinkpad's in being triggered all the time by my thumb as I type"

I know what he means. These designers never learn...

------
cr__
Anyone else feel like we could get along without the "'clit' interface" jokes?

