I had to write a simple script, and decided to do it in CL. It is the
language I know best, and I wanted to learn how to generate standalone
executables in ECL (it was dead simple -- the whole thing is 38K).
I would appreciate if people could comment on the code. It is nothing
complicated and runs fine, I just want to learn the idiomatic way to
use CL for scripting. For example, is line-by-line (see below) the
recommended way to redirect a stream to a file?
For the curious, the script is used to open the body of an e-mail
message from mutt in the browser. I use this to translate the text of
e-mails I get in foreign languages using firefox plugins. Easier than
manual extraction of text & copy-paste. The mutt snippet looks like this:
macro index,pager <F7> "\
<enter-command> set pipe_decode=yes<enter>\
<enter-command> set my_wait_key=\$wait_key wait_key=no<enter>\
<pipe-message>/home/tpapp/software/misc/savebody<enter>\
<enter-command> set wait_key=\$my_wait_key &my_wait_key<enter>\
<enter-command> set pipe_decode=no<enter>\
" "view message body in browser"
Anyhow, here is the Lisp code:
;;;; This simple scripts saves standard input to a randomly generated
;;;; file (starting with *base-path*) and then calls a browser to
;;;; open this file (current setup obviously Ubuntu/Debian specific).
;; working with strings instead of pathnames
(defparameter *base-path* "/tmp/savebody")
;; browser
(defparameter *browser* "sensible-browser")
(defun random-string (length &optional (allowed-chars
"01234567890abcdef"))
"Return a random string of given length, composed from allowed-chars."
(let* ((n (length allowed-chars))
(string (make-string length)))
(dotimes (i length)
(setf (aref string i) (aref allowed-chars (random n))))
string))
(defun open-unique (&key (base-path *base-path*)
(random-length 10)
(max-tries 100))
"Return stream and path."
(tagbody
top
(when (minusp max-tries)
(error "could not open random file - this should not happen"))
(let* ((random-path (concatenate 'string base-path (random-string
random-length)))
(stream (open random-path :direction :output :if-exists nil
:if-does-not-exist :create)))
(unless stream
(decf max-tries)
(go top))
(return-from open-unique (values stream random-path)))))
(defun copy-stream (input output)
"Copy input to output. No error handling."
;; is this the right way to do it?
(tagbody
top
(multiple-value-bind (line missing-newline-p) (read-line input
nil nil)
(when line
(if missing-newline-p
(write-string line output)
(write-line line output))
(go top)))))
(multiple-value-bind (stream path) (open-unique)
;; open file and copy stdin
(unwind-protect
(copy-stream *standard-input* stream)
(close stream))
;; open browser
(let ((args (list (concatenate 'string "file://" path))))
#+sbcl (sb-ext:run-program *browser* args
:search t :wait nil)
#+ecl (ext:run-program *browser* args)))
You really like those TAGBODYs and GOs don't you? :)
> For example, is line-by-line (see below) the recommended way to redirect
> a stream to a file?
That's how I've always done it. I never even realized that READ-LINE
had that second return value; that almost makes up for the way it
mixes optional and keyword arguments.
[...]
Cheers,
Pillsy
By the way, would you mind posting what you did for your ECL exec?
Thanks!
(loop
:do (multiple-value-bind ... )
:while line
:do (if missing-new-line (...) (...))
Obviously I meant "LOOP-FINISH" and "multiple-value-setq"... But one
drawback is you need to make variables separately. So LOOP-FINISH or
just leave your tagbody as is.
See this nice writeup: http://blog.s21g.com/articles/1649
I just fired up an ECL, and used
(compile-file "savebody.lisp" :system-p t)
(c:build-program "savebody" :lisp-files '("savebody.o"))
but apparently you can do much more complex stuff. And the whole
executable is really small. ECL just rocks.
Tamas
> On Dec 4, 5:34 am, Tamas K Papp <tkp...@gmail.com> wrote: [...]
>> I would appreciate if people could comment on the code. It is nothing
>> complicated and runs fine, I just want to learn the idiomatic way to
>> use CL for scripting.
>
> You really like those TAGBODYs and GOs don't you? :)
Yes, guilty as charged :-) I started using TAGBODYs for certain
macros, and then they crept into my simple code too. At the moment I
don't think of this as a stylistic mistake (it seems to be the
clearest way to express what I am doing), but I am open to arguments.
>> For example, is line-by-line (see below) the recommended way to
>> redirect
>> a stream to a file?
>
> That's how I've always done it. I never even realized that READ-LINE had
> that second return value; that almost makes up for the way it mixes
> optional and keyword arguments. [...]
I was thinking about how READ-SEQUENCE could perhaps be more
efficient, with a preallocated buffer that I just reuse. But I guess
that a lot of buffering is done at the OS level already, so maybe
READ-LINE is not that bad. Anyhow, it doesn't matter for my purposes
at the moment.
Tamas
> > On Dec 4, 5:34 am, Tamas K Papp <tkp...@gmail.com> wrote: [...]
> >> I would appreciate if people could comment on the code. It is nothing
> >> complicated and runs fine, I just want to learn the idiomatic way to
> >> use CL for scripting.
> > You really like those TAGBODYs and GOs don't you? :)
> Yes, guilty as charged :-) I started using TAGBODYs for certain
> macros, and then they crept into my simple code too. At the moment I
> don't think of this as a stylistic mistake (it seems to be the
> clearest way to express what I am doing), but I am open to arguments.
I think they're at least as clear as the alternatives.
I eventually wound up writing a little library of functions and macros
for use in scripts (DO-LINES is one of them) and dumping a script-
oriented SBCL image with it and CL-PPCRE and the right command-line
options, for when I need to write scripts. But now I really want to
try out ECL....
Cheers,
Pillsy
Me too, probably we all do - my version of DO-LINES can either do
READs on each and bind variables, or provide line as a string, or
provide lines as a stream, and optionally require a match for lines to
process. It's easy enough to put WHENs in the body to do other
matches.
(with-each-line-do (in-stream
(&rest vars-to-read-from-line)
I also have a script writing macro that transfers command line
arguments into local binidings
Me too - my version of DO-LINES can either do READs on each and bind
variables, or provide line as a string, or provide lines as a stream,
and optionally require a match for lines to process. It's easy enough
to put WHENs in the body to do other matches.
(with-each-line-read (in-strm (val1 val2 ...)
:match "matchstring")
...)
or
(with-each-line-read (in-strm ()
:match "matchstring"
:linestring text-strg)
...)
etc.
I also have a script writing macro that transfers command line
arguments into local binidings
(define-script my-script ((arg1 "-flagForArg1") ...)
... )
So the command line looks like
SBCL -load <file> -eval (my-script) -flagForArg1 <arg1Value>
For more complex programs, the ASDF extensions are probably better:
http://ecls.sourceforge.net/new-manual/ch16.html
I am really happy that you are satisfied with it! It would be nice if
you could share your experience somewhere in the wiki, for instance
opening a page in the success stories section of the wiki
We wrote a quick-and-dirty toolkit to clean up some of the OS
interfacing: we've more or less tried to imitate Perl's operators. Our
toolkit appears to work on SBCL, CCL, and ECL. Making it support
multiple implementations was important, as we're still very much in
pilot mode: we may yet have to make some drastic changes and we need
to be able to move quickly.
We've been taking advantage of the core-dump facilities in CL
implementations for code roll-outs. It makes updates and changes very
simple, especially once we hit a critical mass point.
I'm very interested to hear about your experiences.
> (defun random-string (length &optional (allowed-chars
> "01234567890abcdef"))
> "Return a random string of given length, composed from allowed-chars."
> (let* ((n (length allowed-chars))
> (string (make-string length)))
> (dotimes (i length)
> (setf (aref string i) (aref allowed-chars (random n))))
> string))
In allowed-chars, "0" occurs twice. Why?
Commune-Lisp, like COBOL, has a magical ability to bloat the simplest task.
def random_string length, chars = "0123456789abcdef"
(1 .. length).map{ chars[ rand( chars.size ), 1 ] }.join
end
Guy L. Steele, Jr., July 1989:
I think we may usefully compare the approximate number of pages
in the defining standard or draft standard for several
programming languages:
Common Lisp 1000 or more
COBOL 810
ATLAS 790
Fortran 77 430
PL/I 420
BASIC 360
ADA 340
Fortran 8x 300
C 220
Pascal 120
DIBOL 90
Scheme 50
--
> ;;;; This simple scripts saves standard input to a randomly generated
> ;;;; file (starting with *base-path*) and then calls a browser to
> ;;;; open this file (current setup obviously Ubuntu/Debian specific).
>
> ;; working with strings instead of pathnames
> (defparameter *base-path* "/tmp/savebody")
>
> ;; browser
> (defparameter browser "sensible-browser")
What?
What language is so crude and primitive that goto is required?
>
> (multiple-value-bind (stream path) (open-unique)
> ;; open file and copy stdin
> (unwind-protect
> (copy-stream *standard-input* stream)
> (close stream))
> ;; open browser
> (let ((args (list (concatenate 'string "file://" path))))
> #+sbcl (sb-ext:run-program browser args
> :search t :wait nil)
> #+ecl (ext:run-program browser args)))
>
# Write stdin to a file whose name is randomly generated.
def random_string length, chars = "0123456789abcdef"
(1 .. length).map{ chars[ rand( chars.size ), 1 ] }.join
end
100.times{ file = random_string( 10 )
if not File.exists?( file )
File.open( file, "w" ){|f| f.puts gets(nil) }
exit
end }
puts "Failed to open output file."
--
Ah, yes, cons an interval and a list to then cons a string. And so
readable as well
(defun random-string (length &optional (allowed-chars
"01234567890abcdef"))
(map-into (make-string length)
#'(lambda () (char allowed-chars (random (length allowed-
chars))))))
> I think we may usefully compare the approximate number of pages
> in the defining standard or draft standard for several
> programming languages:
Why don-t you look for the amount of pages trying to explain people
what those specification really meant?
> What language is so crude and primitive that goto is required?
what language is so crude and primitive that its copious syntax looks
like line noise?
--
Raffael Cavallaro
While I can see the appeal of using CL for scripting, I'm interested in
why people choose this approach rather than using one of the lispy
dialects that have been specifically designed for this type of purpose.
I'm thinking of things like guile, lush, rep etc.
Tim
--
tcross (at) rapttech dot com dot au
> While I can see the appeal of using CL for scripting, I'm interested in
> why people choose this approach rather than using one of the lispy
> dialects that have been specifically designed for this type of purpose.
> I'm thinking of things like guile, lush, rep etc.
I cannot see "appeal" of Commune-Lisp for anything.
Here's what the experts say about Commune Lisp:
"an unwieldy, overweight beast"
"A monstrosity"
"sucks"
"an aberration"
"incomprehensible"
"a nightmare"
"no future"
"a significantly ugly language"
"hacks"
"bad"
In context:
Brooks and Gabriel 1984, "A Critique of Common Lisp":
Every decision of the committee can be locally rationalized
as the right thing. We believe that the sum of these
decisions, however, has produced something greater than its
parts; an unwieldy, overweight beast, with significant costs
(especially on other than micro-codable personal Lisp
engines) in compiler size and speed, in runtime performance,
in programmer overhead needed to produce efficient programs,
and in intellectual overload for a programmer wishing to be
a proficient COMMON LISP programmer.
-----
Bernard Lang:
Common Lisp did kill Lisp. Period. (just languages take a
long time dying ...) It is to Lisp what C++ is to C. A
monstrosity that totally ignores the basics of language
design, simplicity and orthogonality to begin with.
-----
Gilles Kahn:
To this day I have not forgotten that Common Lisp killed
Lisp, and forced us to abandon a perfectly good system,
LeLisp.
-----
Paul Graham, May 2001:
A hacker's language is terse and hackable. Common Lisp is not.
The good news is, it's not Lisp that sucks, but Common Lisp.
Historically, Lisp has been good at letting hackers have their
way. The political correctness of Common Lisp is an aberration.
Early Lisps let you get your hands on everything.
A really good language should be both clean and dirty:
cleanly designed, with a small core of well understood and
highly orthogonal operators, but dirty in the sense that it
lets hackers have their way with it. C is like this. So were
the early Lisps. A real hacker's language will always have a
slightly raffish character.
Organic growth seems to yield better technology and richer
founders than the big bang method. If you look at the
dominant technologies today, you'll find that most of them
grew organically. This pattern doesn't only apply to
companies. You see it in sponsored research too. Multics and
Common Lisp were big-bang projects, and Unix and MacLisp
were organic growth projects.
-----
Jeffrey M. Jacobs:
Common LISP is the PL/I of Lisps. Too big and too
incomprehensible, with no examiniation of the real world of
software engineering.
... The CL effort resembles a bunch of spoiled children,
each insisting "include my feature or I'll pull out, and
then we'll all go down the tubes". Everybody had vested
interests, both financial and emotional.
CL is a nightmare; it has effectively killed LISP
development in this country. It is not commercially viable
and has virtually no future outside of the traditional
academic/defense/research arena.
-----
Dick Gabriel:
Common Lisp is a significantly ugly language. If Guy and I
had been locked in a room, you can bet it wouldn't have
turned out like that.
-----
Paul Graham:
Do you really think people in 1000 years want to be
constrained by hacks that got put into the foundations of
Common Lisp because a lot of code at Symbolics depended on
it in 1988?
-----
Daniel Weinreb, 24 Feb 2003:
Having separate "value cells" and "function cells" (to use
the "street language" way of saying it) was one of the most
unfortuanate issues. We did not want to break pre-existing
programs that had a global variable named "foo" and a global
function named "foo" that were distinct. We at Symbolics
were forced to insist on this, in the face of everyone's
knowing that it was not what we would have done absent
compatibility constraints. It's hard for me to remember all
the specific things like this, but if we had had fewer
compatibility issues, I think it would have come out looking
more like Scheme in general.
-----
Daniel Weinreb, 28 Feb 2003:
Lisp2 means that all kinds of language primitives have to
exist in two versions, or be parameterizable as to whether
they are talking about the value cell or function cell. It
makes the language bigger, and that's bad in and of itself.
-----
Guy L. Steele, Jr., July 1989:
I think we may usefully compare the approximate number of pages
in the defining standard or draft standard for several
programming languages:
Common Lisp 1000 or more
>
> I cannot see "appeal" of Commune-Lisp for anything.
>
Obviously you've scanned the internet for comments, and you
are reading this newsgroup. I therefore conclude that CL
must have some considerable appeal to you, contrary to your claim.
Why don't you troll over to some other newsgroup?
For me, it takes a lot of time to learn all the nooks and crannies of
a language, and doing this for the occasional script doesn't justify
the investment. Maybe all those dialects are "lispy", but they have
different names for standard functions, slightly different semantic
conventions, etc, all of which would lead to minor annoyances. So I
chose to roll the thing in CL, and I was pleased, it took very little
time. I will continue to do these things in the future.
Tamas
BTW, lush sucks bigtime, eg it doesn't have lexical scoping. I tried the
it a while ago and it was a major disappointment.
If you want to preserve all characters in the input, opening in binary
mode might be better. Besides this, for purpose of efficiency, read-
sequence with a fixed buffer is much better than read-line. And
mkstemp is essential if your process may run concurrently with others.
(defun open-unique (&optional (base-path "/tmp/"))
;; Using mkstemp ensures that file is not overwritten by a
concurrent
;; process. SBCL has a similar POSIX extension.
(let ((path (ext:mkstemp base-path)))
(open path :direction :output :if-exists :overwrite)))
(defun copy-stream (input output &optional (buffer-length 1024))
(loop with buffer = (make-string buffer-length)
for read = (read-sequence buffer input)
while (plusp read)
do (write-sequence buffer output :start 0 :end read)))
> > While I can see the appeal of using CL for scripting, I'm interested in
> > why people choose this approach rather than using one of the lispy
> > dialects that have been specifically designed for this type of purpose.
> > I'm thinking of things like guile, lush, rep etc.
>
> For me, it takes a lot of time to learn all the nooks and crannies of
> a language, and doing this for the occasional script doesn't justify
> the investment. Maybe all those dialects are "lispy",
Maybe?
They are more "lispy" than the monstrosity known as Commune-Lisp,
COBOL-Lisp, Commode-Lisp, etc.
This isn't comp.lang.commune-lisp, it's comp.lang.lisp.
Get it?
> but they have
> different names for standard functions,
Of what "standard" are you speaking?
This isn't comp.lang.commune-lisp, it's comp.lang.lisp.
Get it?
> slightly different semantic
> conventions, etc, all of which would lead to minor annoyances. So I
> chose to roll the thing in CL, and I was pleased, it took very little
> time.
To a Commode-Lisper, it seemed to take little time to produce that
huge, reeking, steaming pile of spaghetti-code (complete with "goto").
This proves that development in this "language" is always glacially
slow.
> I will continue to do these things in the future.
One who has spent part of his life learning the ugly, convoluted,
massive COBOL-Lisp finds it too painful to face the realization
that all of that time was wasted. He must continue to serve the beast.
What is useful about that comparison?
_______________________________________________________________________________
Don Geddis http://don.geddis.org/ d...@geddis.org
For us, it was somewhat an historical accident: I had an SBCL REPL
open when I was thinking about what I needed, so I started modeling it
there. I intended to port it to Perl, but we decided just to install
SBCL on the target system and see how it worked. It's not broken, so
we haven't fixed it.
From a political standpoint, using anything other than Perl has been a
bit of a risk for us. It's silly, but it's the old "it's to hard to
find someone how knows Lisp" argument. While that may be true, it's
pretty hard to find people who actually know Perl too. A lowest-common-
denominator approach doesn't work very well in ANY language. When we
replace this system, it'll be time to re-examine our use of CL. Until
then, we really need to either stick with CL or go back to Perl.
The main reason for me is that it's not clear that the advantage they
provide is sufficient to make up for the learning curve. It may not be
that steep, but I know CL now, I have CL installed now, and with a few
common libraries and custom macros it's very capable of getting the
job done.
A secondary reason is that if the script starts growing beyond simple
throwaway code, I know that CL is capable of coping. It has good
compilers, a powerful and flexible object system, and conditions,
which I prefer to any other error-handling system I've used.
Why not stick with a language I know and like that I'm confident can
get the job at hand done well, and will still be useful if the job at
hand grows into something more involved?
Cheers,
Pillsy
> Tim X wrote:
>
>> While I can see the appeal of using CL for scripting, I'm interested in
>> why people choose this approach rather than using one of the lispy
>> dialects that have been specifically designed for this type of purpose.
>> I'm thinking of things like guile, lush, rep etc.
>
> I cannot see "appeal" of Commune-Lisp for anything.
Fine, I didn't ask if you liked it or not. I asked those who do why
they chose it rather than one of the other lisp dialects. I'm not
interested in your unoriginal re-hashing of uninteresting irrelevant
quotes in an attempt to either start a flame war or push your own
boring agenda.
thanks Tamas. I'm interested as I'd prefer to write scripts in a more
lisp like environment. I've been debating which direction to go. To be
honest, I've been leaning towards using guile over common lisp as it
seems a bit more suited to the types of things I often need to do in a
script, is more commonly found already installed on GNU Linux based
systems and possibly is a little 'lighter' than many of the CL
implementations.
Lush was another one I was wondering about. Lack of lexical scoping is
possibly an issue, not sure. need to give it some thought. Would
certainly be an issue for anything larger/complex, but possibly not a
big issue for many smaller tasks.
What I really want is something fairly light-weight that I can use to do
fairly simple scripts in. While shell, perl etc all work fine - I'm
interested to see what benefits there may be in not having to switch
between different paradigms as much.
thanks. I can appreciate your point that you had the sbcl repl there and
so just sarted working on it from there. I've done similar in the past.
I also agree with some of what you say about perl - while there may be a
lot of people who claim to know perl, I've seen some bloody awful perl
that takes a lot more work to maintain/fix. To be honest, I think any
programmer that enjoys what they do and has an actual interest will have
no problem with any language - there may be a bit off learning and there
may be a bit of frustration if its a different paradigm they need to get
use to, but realy, the old "can't use that because we can't get any
skilled staff in that language' argument really is a cop out. What it
really means is that we don't want that language because we really only
employ uninspired code monkeys that can pump out crappy ugly brute force
solutions. instead of 5 really good programmers, its easier for us to
employ 20 crap ones and pay them bugger all - beside, by the time anyone
realises the false economy we are setting up, I will have moved on to
that next job, so who cares.
All points I can appreciate. To some extent, its the having to learn
another lisp dialect with its own odities that has me wondering if the
benefits are worth it. Most of the scripts I'd be looking at would stay
as fairly simple/small scripts. However, there is a feeling of using a
sledge hammer to crack a walnut. I also work on many different systems,
most of which will not have Cl installed, but nearly all of them have
guile and most have rep. Looking at the APIs/libraries, they also all
seem to have more extensive 'standard' support for the sorts of common
things you would frequently do as part of scripting. I'm not saying you
can't do this with CL, but rather how easily can I justify using CL if
other alternatives come with much of what I would need as
libraries/apis. There is also some appeal in using something different
to get another viewpoint on how things are done in other lisp like
languages.
On the other side is the fact that Cl is a language I've used and am
familiar with (at least more familiar than I am with the alternatives)
and so the learning curve is less.
> thanks Tamas. I'm interested as I'd prefer to write scripts in a more
> lisp like environment. I've been debating which direction to go. To be
> honest, I've been leaning towards using guile over common lisp as it
> seems a bit more suited to the types of things I often need to do in a
> script, is more commonly found already installed on GNU Linux based
> systems and possibly is a little 'lighter' than many of the CL
> implementations.
Guile could be OK, if you make use of the extended features.
Nevertheless, I think that you should consider CL too. It is a much
more mature language, and the current free implementations are really
good, producing fast programs. "Lightness" is not really an issue: CL
is about the same size as a mature Scheme implementation with all the
bells and whistles, except that you won't be able to change
implementations so easily. This is an important thing: I have been
playing around with ECL in the past few days and found that most of
the code I used on SBCL runs with a few modifications (changing some
implementation-dependent stuff, and correcting some false assumptions
I had about CL :-). This is a big advantage. And installing a Lisp
on most modern distros takes a few seconds (eg apt-get install sbcl).
> Lush was another one I was wondering about. Lack of lexical scoping is
> possibly an issue, not sure. need to give it some thought. Would
> certainly be an issue for anything larger/complex, but possibly not a
> big issue for many smaller tasks.
Lush is really an archaic language. From the Lush FAQ: "The design of
the interpreter is pre-Scheme/pre-CommonLisp, and it shows." I think
it is a dead end, and it is a waste of time to learn things like that.
> What I really want is something fairly light-weight that I can use to do
> fairly simple scripts in. While shell, perl etc all work fine - I'm
> interested to see what benefits there may be in not having to switch
> between different paradigms as much.
I never really understood what people mean by "light-weight". Small
memory footprint? Small standard library? Few language features?
Ease of deployment? The way people use the phrase suggests that they
are thinking of some trade-off, ie "non-lightweight" languages having
some disadvantages. I don't really see any issues with CL.
Cheers,
Tamas
> benefits are worth it. Most of the scripts I'd be looking at would stay
> as fairly simple/small scripts. However, there is a feeling of using a
Haha. A lot of scripts start that way, and later the authors find
that they have created a 1000 line monster in Bash :-)
> sledge hammer to crack a walnut. I also work on many different systems,
Don't think of CL as a sledge hammer. Rather as a kind of special
putty that you can use to design the perfect walnut cracker for the
job. The putty is cheap, and is easy to shape, but if required, it
can become hard as steel. It can also be used to create factories
that make walnut crackers customized to each walnut. And the putty is
costless. The only disadvantage is that it gets under your
fingernails, which you have to clean with a brush at the end of the
day :-)
Tamas
I second this - a couple macros (really about five in my case), and
from then on I've been scripting away for years...
Scripting has domains, like any other programming. (eg, I work for a
video processing company therefore I usually process video data.)
That means even if you use a dialect specifically designed for
scripting, you'll still add in your five macros (or fcns) that suit
your needs.
The CL basis also means that I can fuse my scripting with
PortableAllegroServe and have a GUI that the whole office can access.
(I'm sure many CL-ers would prefer Hutchentoot...)
> A secondary reason is that if the script starts growing beyond simple
> throwaway code, I know that CL is capable of coping...
I second this, too - more than once a script has grown into an
application that takes
over fine grained data processing itself rather than calling out to
other programs.
> Guy L. Steele, Jr., July 1989:
>
> I think we may usefully compare the approximate number of pages
> in the defining standard or draft standard for several
> programming languages:
>
> Common Lisp 1000 or more
> COBOL 810
> ATLAS 790
> Fortran 77 430
> PL/I 420
> BASIC 360
> ADA 340
> Fortran 8x 300
> C 220
> Pascal 120
> DIBOL 90
> Scheme 50
If Ruby would have a standard which documents all functions in
detail that ship by default with Ruby, how many pages would that
fill? Less than 3000? I don't think so.
What does this tell us about Ruby?
André
--
Lisp is not dead. It’s just the URL that has changed:
http://clojure.org/
That's not measured in pages, but Kloc's. :)
I tried Guile, but it was very difficult to get it to install on my
Mac. That's essentially a deal-breaker for me, as I work on Mac and
Linux more-or-less interchangeably.
Gambit Scheme is very promising as a "scripting language", and it may
eventually gain enough support around here to end up as the lisp of
choice. There is interest in Gambit outside my team, and that sort of
thing might make it gain some traction. I've used it some on my
personal machines, and it's really a very good language for hacking
out solutions.
But CL works fine as a scripting language with a little investment up
front. And as others have pointed out, a very little attention to
portability makes it a snap to get it to run more or less uniformly on
several platforms.
The ability to ratchet up a script iteratively helps too. E.g., in one
place, we started out passing around plists; once we had a better feel
for what we wanted, we started replacing plists with structs. The code
got more formal as the "project" went on, and CL kept up. Perl could
have kept up with it, but it would taken more effort to refactor.
Those changes just seemed natural in CL.
Ditto, especially once I found out how well Linux & FreeBSD cache
mmap'd files such as CMUCL's executable and core image files -- the
startup times [except maybe the very first one after a reboot] are
plenty fast for "simple scripting":
$ cat test_cmucl
#!/usr/local/bin/cmucl -script
(format t "Hello, world!~%")
$ time ./test_cmucl
Hello, world!
0.006u 0.006s 0:00.01 0.0% 0+0k 0+0io 0pf+0w
$ time-hist ./test_cmucl
Timing 100 runs of: ./test_cmucl
92 0.012
8 0.013
$
IMHO, 12 milliseconds is an acceptable penalty for getting to use
full Common Lisp for my scripting.
CMUCL has one more feature that makes "simple scripting" even more
"light-weight": lazy parsing of function bodies. That is, when you do
a top-level DEFUN, the body isn't "converted" [macroexpanded and then
parsed into tree code for the interpreter] until you call it the first
time [or reference it as a function value]. This means that scripts
that define a whole bunch of functions but only call one or two of them
[depending on command-line args] run faster than if the whole script
were converted [or worse, *compiled*!] before anything is executed.
And for any really heavy lifting a script can always REQUIRE previously-
compiled code. [Or even *be* previously-compiled code, see "p.s." below.]
As a result, I've been using CMUCL for almost all my miscellaneous
"scripting" for a number of years. At any point in time, I generally
have ~60 CMUCL "script" in my "$HOME/bin/" directory.
-Rob
p.s. True, stock CMUCL doesn't support the "-script" option demonstrated
above. See <http://rpw3.org/hacks/lisp/site-switch-script.lisp> for a
plugin that adds that [and also supports slapping a "#!" line on CMUCL
compiled FASL files to make *them* "executable" as well].
-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
Thanks Rob. Actually, it was some of the stuff you posted a while back
re: using Cl for scripting that provided the seed for me to consider
using Cl in such a way. At the time, I was quite impressed to see how
clear and straight-forward some of it was.
Most of the replies in this thread pretty much confirmed the way I was
leaning already. In some of my home projects, I've used CL where once
upon a time, I would have used perl or a shell script.
I'd now like to start using CL at work more. I've done so in a limited
way with a couple of application prototypes that worked quite well.
However, using it for some scripts may be a harder battle to win, so I
was really looking for some counter arguments. Not sure if it will do
any good - its still mainly FUD I run into rather than anything of real
substance. The difficulty with using it for scripting is that a lot of
others will see what I'm doing. With theprototypes I've done, CL was OK
because if it all whent badly, it was just my head that would roll.
However, letting CL get into core areas is another story.
My other concern is my CL skill level. While I've been using CL fairly
regularly for a few years now and have done some successful prototyping,
I've not yet done anything that needs to be 'production' quality. the
rate at which I continue to learn new stuff makes me think I've still
got a long way to go. At the same time, this is also part of what makes
me enjoy CL - unlike other languages I've worked in, CL continues to
both challenge me and still gives me at least a couple of 'aha!' moments
a week. this can be a little dangerous. While I'd enjoy spending weeks
or months or possibly even years working on something and building my
knowledge of CL, I'm still expected to deliver something of real quality
within a limited time - a balancing act I'm not always good at.
Unfortunately, I also live in a rural area with no lisp user groups,
which means nobody to discuss ideas and problems with in an informal
relaxed environment, which I think can often help keep a good
perspective.
Depending on what kinds of scripting you're talking about, it's probably
unlikely that you'll be doing much that stresses CL or your expertise in
it. You'll need a good layer of arg-parsing and OS-interface stuff so
you don't keep bashing the same corner cases again and again, but
otherwise...
One big advantage of CL for scripting, in my experience, is that the
performance is often sufficiently improved over perl or shell scripts to
make a qualitative difference in the kinds of things you can do. You
might want to look at something where perl currently takes too long.
paul
I've been able to get away with it pretty much unopposed in a lot of
marginal areas, places that don't directly challenge the expertise of
others in their preferred languages. For example, at my most recent PPoE
I got CMUCL included in the approved binaries that build tools could
use, since I had a couple of CL scripts that parsed ad-hoc documentation
(produced by others) to generate C headers & data initializers for a
certain Linux kernel device driver. Nobody had a problem with that,
since it was an automatic part of the system build process that they
didn't have to look at (as long as it "just worked", which it did).
Another area (as I've written about here previously) is user-mode
debugging tools for hardware: there just aren't any "standard" tools
for such things, so nobody cares that I write mine in CL.
Yes, some of that *did* end up "leaking" into "core areas", but only
indirectly, e.g., I coded the hardware debugger script so that its
internal pre-compiled functions could be evoked from the shell command
line, and the Manufacturing & Support folks ended up using some of those
functions when testing/diagnosing the product, e.g.,
$ hwtool ecc-scan 0 0x40000
...[Scan a range of NVRAM for ECC errors]...
$ hwtool i2c-reset
...[Do a brute-force sledgehammer reset of the I2C bus.]...
$ hwtool pcix-strength 1 8 32 32
...[Tweak the drive strength of the PCI-X bus interface]...
Note that "hwtool" *needed* the speed of compiled code [since it was
frobbing hardware bits directly!], so it couldn't have been done in,
say, Tcl or other "interpreted" languages. [At least, not without
writing a bunch of special C code to load into the Tcl image.]
+---------------
| My other concern is my CL skill level. While I've been using CL fairly
| regularly for a few years now and have done some successful prototyping,
| I've not yet done anything that needs to be 'production' quality.
+---------------
Again, if you develop your skills doing stuff that, while it needs to
be done to get the main product out the door, is in areas that don't
directly challenge the expertise of others, your skill & confidence
in CL will naturally progress.
In any case, the definition of "production quality" can vary wildly
depending on the situation. My *first ever* major piece of CL code
[a web application server and SQL-based app behind it] ended up going
into production simply because there wasn't any other viable way to
get the job done in time. [It was a volunteer effor for a non-profit,
but still...]
I'm not suggesting you ship stand-alone commercial software products
that you don't feel comfortable with, only that there are aspects of
commercial enterprise where some risk with new tools is acceptable
[e.g., "infrastructure" stuff].
-Rob
thanks Rob. Your points are all good ones and your approach very much
mirrors how I plan to proceed. If nothing else, it will make the job
more interesting!