
Scripting in Common Lisp - mr_tyzic
https://ebzzry.io/en/script-lisp/
======
hobgoblin1234
It's not common lisp, but most of my Emacs use has been as a shell scripting
alternative.

Workflow being based around interactive improvement of a script from a simple
interactive-macro through to things that are effectively done as shell
scripts.

The end result is something that can be run as a shell script, although
translating to another language i.e. CL, Ruby, Go, etc. is the usual path for
me to make better use / performance.

The old joke about Emacs being an operating system that needs a good editor is
on need of a reboot.

Emacs is an interactive computing and development environment with half a
dozen good text editors.

Factual isn't as funny though.

~~~
anon10938750136
Emacs is my favourite software, but elisp is one of my least favourite lisps.
The other day I stumbled upon Shen language after some years and noticed it
now has BSD license and an elisp implementation. I tried to add it to
spacemacs and, to my surprise, it just worked. When I have some spare time the
next thing I'll do will be to learn some Shen and see if for shell scripting
it is an improvement over elisp.

~~~
serialdev
Wow! thanks for pointing out there is an Elisp Shen implementation!

~~~
gglitch
Erlang too, fwiw.

------
aasasd
I'm somewhat baffled that Hy didn't gain any traction.

Let's review the facts:

\- A lot of people love Python.

\- Plenty of people love Lisps, afaict with a sizeable overlap with the first
group.

\- Hy, which is a Lisp on the Python platform, has no popularity whatsoever
compared to other Lisps like Clojure.

The heck?

~~~
aidenn0
Hy may have changed since I last looked at it, but when I had looked at it, Hy
was more of an s-expression syntax bolted onto python.

Some random thoughts on why this matters:

Clojure is very much _not_ a thin s-expression layer for java; that would have
been terrible (Python is more like lisp than java which is why Hy is tenable
whatsoever).

In particular, CL style macros with out some first-class concept of symbols
are a footgun[1], but I'm sure I could find other issues.

Another item that can't be ignored is that Hy selected the clojure syntax,
which is going to be a turn-off for anyone coming from Lisp or Scheme.

Finally, there are hundreds of thin s-expression layers on top of existing
languages, and most of them are terrible; Hy has done nothing to stand out
from this crowd, so most lispers will not look closely at it.

1: See
[https://youtu.be/cPNkH-7PRTk?t=4786](https://youtu.be/cPNkH-7PRTk?t=4786) for
Rich's discussion of how he managed this issue in Clojure.

~~~
aasasd
I'm actually interested in what other problems make Hy and similar languages
infeasible. Even if a brief overview, as food for thought on the matter.

~~~
aidenn0
They aren't infeasible; a real lisp could target Python. However, just bolting
on lisp syntax to python ends up in an uncanny valley of "not python" and also
"not lisp."

I would actually recommend watching the entire youtube video I posted earlier
(Clojure for Lisp programmers). Rich talks about a lot of the design decisions
that went into Clojure. Some decisions I agree with, some I don't, but there
was thought into creating a unified whole _and_ he was clearly aware of work
that had gone before, even when he chose to do things differently.

Note also that Clojure was his _third_ attempt at doing a lisp/java interop,
so there was a lot of previous experience backing many of those decisions.

Lastly, it's _really_ easy to make a bad lisp-frontend for a language and a
massive amount of work to make a good one. This is well known by anyone who
has been in the lisp community for a while (if for no other reason than they
themselves have made a bad one) so for better or worse, such frontends tend to
be ignored.

~~~
kazinator
A good way to target a Lisp to Python might be to forget the Python AST and
target its virtual machine directly.

The compiler could itself be written in its own target dialect, and
bootstrapped with some existing Lisp.

------
jolmg
This is the first time I've seen content provided in Esperanto like this.
Pretty cool.

Even cooler is that the Esperanto version seems to be the original, and then I
suppose it was translated to English a year later.

Huh, now I see the whole blog defaults to Esperanto.

I wonder how much of the net takes that stand of Esperanto-first.

------
kimi
I use scripting extensively in Clojure, both on the JVM and on Planck (JS).
You have the choice of slower startup times but a ton of libraries to do
anything you want, versus short start-up times but less libraries, using the
same language. I use [https://github.com/l3nz/cli-
matic](https://github.com/l3nz/cli-matic) as a wrapper and it works on both.

~~~
zebraman
Using modern Clojure's clj is so much easier than the CL procedure.

~~~
eggy
Until you have to deal with unintelligible Java stack traces.

~~~
islon
They are intelligible if you know Java and Clojure 1.10 improved stack traces.

~~~
TeMPOraL
Yup.

Common Lisp stack traces tend to be no better, anyway, at least with SBCL and
CCL. Up to 90% of a trace you see in the inspector can be compiler/runtime
infrastructure. Given that macros don't show up, only their expansions, the
backtrace can be full of stuff like SB-KERNEL:%FUNCTION-THAT-I-DONT-RECOGNIZE-
BUT-MACROEXPANDED-FROM-SOMETHING-I-KNOW.

Like in Java, most of the time the top few lines are the ones related to your
code, but just like in Java, there are exceptions, in which the source of the
problem ends up in the middle of the backtrace, surrounded by infrastructure
frames.

------
codemac
This is a great guide, and solving quick real problems is how people get
started programming in general.

I think seeing scsh for the first time made me realize just how good & bad
shells are.

~~~
rauhl
> I think seeing scsh for the first time made me realize just how good & bad
> shells are.

Oddly enough, just last week I was trying to do some scsh-like things with
Common Lisp, and I ended up banging my ahead against its case-mapping
behaviour (Common Lisp upcases input by default).

There’s definitely an opening for a Lisp version of something like scsh.
There’s some tricky stuff to be aware of (e.g. SBCL’s RUN-PROGRAM puts
processes in a different process group, which mean C-c does the wrong thing),
but done well it could really be awesome.

~~~
aidenn0
Setting case to invert by default fixes a lot of those problems (you just
invert the case again before passing the string onto the shell).

Also, for doing many shell things fork/execv is the only thing that gives you
sufficient control, so I suggest avoiding run-program and using those instead.

------
kakw
I recommend roswell
[https://github.com/roswell/roswell](https://github.com/roswell/roswell) .

------
agumonkey
Pretty nice considering the speed and capabilities of [SB]CL relative to other
mainstream scripting languages.

~~~
TeMPOraL
I shy away from CL scripting due to runtime startup time, though if you
compile it down to binary it's manageable (if one doesn't mind a 50MB
executable).

That said, I wonder why nobody tried a "hybrid" solution, in which the multi-
call binary is implemented as a resident, self-restarting process, that
executes each request in a lightweight sandbox. Actual shell commands would
then be implemented as a request to that process, and incur only the costs of
setting up IO streams on startup.

~~~
agumonkey
what CL implementation ? sbcl ? I don't know what's the leanest CL out there
(even if not the most featured, since we're talking about short scripts)

~~~
TeMPOraL
SBCL mostly. I probably should give GCL a try here. SBCL is fast as hell once
it starts up, and _bare_ SBCL starts up quickly, but once you add Quicklisp
and start loading external systems, the startup time gets noticeable - though
again, this gets reduced quickly iff you dump a binary of your image with
dependencies already loaded. The one CL script I wrote that I actually use[0]
(for controlling Hue lamps) I have dumped into binary just to cut down startup
time to below the point I notice.

\--

[0] - [https://github.com/TeMPOraL/hju/](https://github.com/TeMPOraL/hju/)

~~~
agumonkey
SBCL does have a tiny delay (akin to a raw JVM). CLISP is near interactive
though. But that's without quicklisp.

------
jstewartmobile
Where is your piping example?

~~~
junke
I do not use cl-launch, but for some tasks at job I wanted to pipe programs,
and just wanted to try implementing something myself. The with-pipeline macro
creates temporary fifos to connect programs and threads (random example):

    
    
        (let ((counter 0))
          (with-pipeline ()
            (program "ls" "-1la")
            (lambda-line (line)
              (format t "~x~%" (incf counter (length line))))
            (program "sed" "-n" "s/A/_/g ; /__/ p"))
          counter)
             

Outputs and return:

    
    
        2__8
        15399
    

Macroexpansion:

    
    
       (catch :pipeline
         (block nil
           (let* ((#:input%2195 (pipeline::ensure-stream nil :input nil))
                  (#:output%2196 (pipeline::ensure-stream t :output nil))
                  (#:error%2197
                   (pipeline::ensure-stream :output :error #:output%2196)))
             (let ((#:g2199 (program "ls" "-1la"))
                   (#:g2200
                    (lambda-line (line)
                      (format t "~x~%" (incf counter (length line)))))
                   (#:g2201 (program "sed" "-n" "s/a/_/g ; /__/ p")))
               (pipeline.pipes:with-pipes% (#:pipes2198 2)
                 (lastcar
                  (mapcar #'pipeline.filters:clean
                          (list
                           (pipeline.filters:spawn #:g2199 :input #:input%2195 :output
                                                   (pipeline.pipes:pipe-out
                                                    (svref #:pipes2198 0))
                                                   :error #:error%2197 :wait nil)
                           (pipeline.filters:spawn #:g2200 :input
                                                   (pipeline.pipes:pipe-in
                                                    (svref #:pipes2198 0))
                                                   :output
                                                   (pipeline.pipes:pipe-out
                                                    (svref #:pipes2198 1))
                                                   :error #:error%2197 :wait nil)
                           (pipeline.filters:spawn #:g2201 :input
                                                   (pipeline.pipes:pipe-in
                                                    (svref #:pipes2198 1))
                                                   :output #:output%2196 :error
                                                   #:error%2197 :wait t)))))))))
    

[https://github.com/christophejunke/pipeline](https://github.com/christophejunke/pipeline)

