Subject: Re: Harlequin vs. Allegro From: Erik Naggum <clerik@naggum.no> Date: 1998/06/24 Newsgroups: comp.lang.lisp Message-ID: <3107671508027284@naggum.no> * Zeno the Anonymous Poster | the moment, is it possible to create a small application with Lisp which | could be distributed over the web? yes, of course. the question is: what does the recipient need to use it? for some languages, the user needs to have installed *huge* libraries of run-time support systems, a *massive* operating system, and have contributed a lot to Bill Gates's now 50 billion dollar fortune. for other languages, the user needs to have installed libraries of run-time support systems, an operating system, and not have contributed to any top-20 fortune list. that's the difference between VB and Common Lisp, for instance, and all you can really argue is "but I already have all the stuff for VB installed". the question is then why you do that instead of the much more sensible thing, and perhaps why you should stop installing stuff now, just _before_ you get yourself a Common Lisp environment, too. | Let's say, you wanted to create a small utility which would search the | files on Win95/NT computers and find duplicates, then provide a list to | the user allowing them to delete duplicate files as they see fit. Can | you do this with Lisp? Can you do this with Allegro Common Lisp? Are | small utilities like these necessarily large programs because of the size | of Lisp itself? yes, of course you can. however, how do you run this utility? with the VB approach, you basically run the huge run-time system from the command line or GUI environment and feed it some input that happens to be your "small utility". with the Common Lisp approach, you basically run the Common Lisp environment, then invoke the function from the listener. (sufficiently advanced Lisp listeners are indistinguishable from GUI's.) Emacs users everywhere run some surprisingly large programs in Emacs and don't seem to have any craving for an "executable" version of, say, Gnus. the same applies to other Lisp software. of course, you _can_ run a separate Emacs that you dumped with Gnus and argue against the size of the damn thing, but why bother? running Gnus inside your already running Emacs is _easier_. an that's the issue with Common Lisp, too: it's just a whole lot easier to have a Common Lisp environment running and then do all the cool stuff in it than to run whatever other environment your computer came with and fire up huge run-time systems every time you need the services of a small utility. * Pierre Mai | I hate repeating myself, but: | | 1) For very small, _self-contained_ utilities, Common Lisp may simply | not be the right language. I disagree profoundly. first, "self-contained" is no longer a valid concept when used about general purpose computers like PC's or workstations -- the interdepencies between individual programs and the operating system environment has been so blurred as to make the choice between "data for the run-time system" and "executable program" all but meaningless. second, I run self-contained utilities in Common Lisp all the time, if I may twist your words a little: I call them "functions". | On Unix you'd rather either use the built-in utilites, or perl/scsh. really? if you consider perl and scsh "utilities" to be _small_, just because they fit in very small source files, I'm afraid that you really don't see the whole picture. Common Lisp programs are easily shorter than Perl and Scheme programs, so I would imagine that if you could run Common Lisp programs with the hash-bang convention, they'd be even smaller than "very small", since you completely ignore the *enormous* costs of the Perl and Scheme Shell execution environment. | Another posibility are several Scheme implementations, which are | able to produce small stand-alone images... let me tease you a bit: do you mean images from which you can boot your computer, like form a floppy disk? that is the truly "stand-alone" program. dedicated software like Internet routers running on PC's meet this definition of "stand-alone", and they do exist -- they aren't even very hard to build -- all it takes is linking in the basic facilities from a library and asking the linker to write a slightly different file format that the boot loader recognizes. matter of fact, the whole operating system meets this definition of "stand-alone". (however, they aren't _small_, anymore, and frequently need more than one CD-ROM. :) incidentally, no Scheme implementations I know of can write such images. | Things look very different if you want the utility to work in CL, | as a utility for developers, since then the user already has a CL | environment, and you only distribute the (compiled) files. but the user _always_ "already has" the prerequisite environment! this actually reminds me of a user who sued an Internet provider over here because their marketing line for a "complete" package of software and hardware for Internet users, "all you need to use the Internet", was _entirely_ false -- you actually needed a whole _computer_ (and it had to pledge allegiance to Bill Gates to boot, but that didn't seem to worry anybody) and only _then_ did you have all you needed. if you don't have a Common Lisp environment, the question should _not_ be: "how can we make our software available to users without computers?", but "how do we make them understand that they need a computer _with_ a Common Lisp environment?" the problem, then, is only selling them the first Common Lisp application that needs a Common Lisp environment. once he's got it, it's just rolling them in! | So this all boils down to using the right tools for the problem: Use CL | where it works best: In medium[3] to large applications that work on | complex data and/or implement complex functionality/logic. CL is _not_ | the language of choice for small, stand-alone applications, which could | even be implemented in perl or VB and still be maintainable. wrong! this is _so_ wrong! *cringe* (uncringe, breathe.) nothing ever _starts_ large except in some extremely specialized areas. all over the place, you're expected to buy the large starting point and then make some small application on top of it, then get more funding and boss approval and peer recognition and all that, and _then_ you let the thousand cancers grow. Perl is *huge*, VB is even bigger. on top of these monsters, you can write a small program that does something useful. Common Lisp is not different in any possible regard, except it makes for smaller, more elegant, and more playable toys. however, when Perl or VB code gets bigger than is good for them (a screenful in my opinion, but people seem to have very large screens these days), two things happen at once: (1) they realize they should have used a better language, and (2) they can't use a better language, for several reasons: (a) they don't know the better language because they have never started using it for small toys, (b) they don't have the better language available because they were encouraged to use Perl and VB for "small" applications and never got any experience with it, (c) they don't have the chutzpah to ask their boss for the better language that they don't know anything about because they didn't dare realize that their application would work on complex data and/or implement complex functionality/logic until just after they ran out of the chutzpah needed to say Perl or VB (and they) could handle it, (d) it means scrapping working code, and finally, (e) because the change to the better language is no longer incremental, like all the other changes to code they have worked on or written have been. what happens to a Common Lisp system that grows? with a marginal amount of nurturing by people who care about elegance, it stays elegant, and it acquires functionality the same way that the Common Lisp language did: by careful consideration of the costs of changing one's ways as well as good design in general functionality where observed necessary. I think a Common Lisp programmer who is able to think in the terms of Common Lisp the Standard (i.e, ANSI X3.226) and who is not afraid to write functions and macros and interfaces that need specification on the same level of precision as the standard facilities will necessarily write elegant code, and not end up with a hodge-podge of special-cased crud that fails to achieve abstraction by it's sheer lop-sided overweightness, which is what happens to the cancerously growing masses of code in languages that had all their abstraction done by the language designer and then you just get to use whatever they left you (C, C++, Perl, VB, etc, etc). my favorite examples of just this kind of development on top of Common Lisp are the MOP, the Gray Stream proposal, and logical pathnames (with which I've spent the last few days struggling...). one could view CLOS as just such an extension to the first Common Lisp language. their commonalities are: being well-integrated, solving very hard problems elegantly through abstraction in the right places, and exposing no essential differences between "application", "extension", and "language". properties like this is why I like Common Lisp over languages that make a tremendous effort to separate the three categories, and I want to write my code the same way, potentially leaving something of lasting value, not just some piece of code that "works". what makes this both possible and impossible, however, is that adding to the Common Lisp heritage is not for random enthusiasts in the pre-burn-out phase, but for those who are willing to grok the language (if "grok" is still recognized as a word by our younger audience -- Merriam-Webster's Collegiate dropped it between the Ninth and the Tenth Edition -- boo hiss). this takes a lot of time and effort and is a sometimes humbling experience, but there is also no shortage of people a lot smarter and more experienced than yourself who have gone before you. (at least in my experience.) making small steps in this way in Common Lisp has given me a lot more pleasure than most of the stuff I have ever been proud of in C or under Unix. in contrast, just about any newbie can make suggestions for (real and important) improvements to Perl or C++ and actually get them into the language! knowing how cool it is to see "my feature" in a large system like Emacs, it's no wonder that Perl enthuiasts feel the way they do about their tools, but I contend that they would feel a _lot_ better if they had not had to invent the mindless kludges that made some trivial thing marginally easier, but could have used a well-functioning system from the start and could write some small piece of code that did something neatly and cleanly that would otherwise be _very_ ugly. my suggestion is simply: start with small problems, not with large ones. (and don't start with problems that you think are ideal for Perl or VB -- you'll find that it was the language mindset that defined "ideal", not the problem itself or indeed _any_ part of its nature.) the goal is to gain experience, just like you gained experience with Perl or VB: by doing little things that looked sufficiently fun. don't think for a minute that "what's cool in Common Lisp is different from what's cool in Perl or VB", but ask "what's cool in _this language_?" (which is actually what you should think for any language, or tool, or system, or whatever). if you start off thinking "how will I solve this Perl problem in Common Lisp?" you will only find that the Perl mindset is your limitation, and you will probably blame Lisp, not the least because Larry Wall does. for your entertainment, a small piece of code that almost completely hides the annoying habit of at least one operating system to delimit lines in text files with CRuft instead of just newline characters, and for the moment ignoring the fact that this problem should have been solved even more elegantly by the file transfer/sharing software... ;;; administrivia (defpackage "STREAM-EXAMPLE" #+allegro (:use "COMMON-LISP" "STREAM" ;the Gray stream proposal "CLOS") ;the Meta-Object Protocol #-allegro (:use whatever is appropriate)) (in-package :stream-example) ;;; a typical simple mixin filter class and methods. (defclass macintosh () ()) (defmethod stream-write-char ((stream macintosh) (character (eql #\newline))) (call-next-method stream #\return)) (defmethod stream-read-char ((stream macintosh)) (let ((character (call-next-method))) (if (char= character #\return) #\newline character))) ;;; interface function to dynamic stream class creation (defun push-mixin (mixin stream) "Push the MIXIN (named by a symbol) on STREAM (a stream object). Actually, dynamically create, if necessary, a subclass of MIXIN and the current class of STREAM, and change the class of STREAM to this class." (let* ((name (concatenate 'string (symbol-name mixin) "+" (symbol-name (type-of stream)))) (symbol (intern name #.*package*)) (class (or (find-class symbol nil) (ensure-class symbol :direct-superclasses (list mixin (class-of stream)))))) (change-class stream class))) ;; typical usage (with-open-file (stream "some-file-from-a-mac" :direction :input) (push-mixin 'macintosh stream) (read-line stream)) I think this is pretty cool, not the least because I can string together all sorts of filter functions and mapping tables with mixin classes like this, bunch up a number of thus created streams in a broadcast stream and write to a whole bunch of files at once in places in the networked file system I have relegated to the logical pathname stuff to select for me. an 8,000-line piece of C code got replaced by 400 lines of Common Lisp in this fashion. (yes, it's also faster. :) #:Erik PS: the other articles that need replying to will be answered shortly. -- http://www.naggum.no/spam.html is about my spam protection scheme and how to guarantee that you reach me. in brief: if you reply to a news article of mine, be sure to include an In-Reply-To or References header with the message-ID of that message in it. otherwise, you need to read that page.