don't think of "lisp" as a language, think of it as another branch in the evolutionary tree of programming languages. the "lisp is great" people are not saying that any given lisp-based language is the be-all and end-all of programming languages, they are saying that the lisp branch is the best branch to explore.
Part of the perfection is that it can relatively easily be reimplemented and varied. Clojure uses this to leverage the JVM and adds support for concurrency etc. When people say Lisp is perfection they don't mean that one particular Lisp is perfection, but the idea of Lisp is perfection - or damn close to it.
> So why are we trying to differentiate lisps, given that they're excellent?
I think the best way to look at this question is to think about who "we" is.
Are the sum total of programmers using lisp coordinating to make a final decision on how many lisps there should be? Certainly not. I don't know all the people using lisp, and I never will. So it doesn't make sense that they're trying to do anything. This community isn't defined in such a way that it's meaningful to talk about directed effort.
There are plenty of reasons to make different lisps without implicitly disparaging the functionality of existing lisps. Perhaps you want to learn about how lisps work by implementing your own. None of the existing languages would help you there. Perhaps you simply have a sense of curiosity about a language feature, and want to implement it somewhere. A less mature lisp might help, if the codebase is smaller and easier to reason about. Perhaps you need to use the JVM for business reasons. Perhaps the JVM is too much for the hardware you're running on, and need a C variant. Perhaps there's one with a particularly useful FFI to another language.
Why are Lisps excellent, is a better question. They work well with structure editors, and they allow for very powerful metaprogramming of the language to add in any needed features.
Those forms of excellence do not put Lisp on the JVM, nor give emphasis to multi-threaded, functional programming. Hence, Clojure.
"Customers and stakeholders have substantial investments in, and are comfortable with the performance, security and stability of, industry-standard platforms like the JVM. While Java developers may envy the succinctness, flexibility and productivity of dynamic languages, they have concerns about running on customer-approved infrastructure, access to their existing code base and libraries, and performance. In addition, they face ongoing problems dealing with concurrency using native threads and locking. Clojure is an effort in pragmatic dynamic language design in this context. It endeavors to be a general-purpose language suitable in those areas where Java is suitable. It reflects the reality that, for the concurrent programming future, pervasive, unmoderated mutation simply has to go.
Clojure meets its goals by: embracing an industry-standard, open platform - the JVM; modernizing a venerable language - Lisp; fostering functional programming with immutable persistent data structures; and providing built-in concurrency support via software transactional memory and asynchronous agents. The result is robust, practical, and fast."
Of course, one can run Common Lisp on the JVM and one can integrate CL with the JVM; one can add immutable persistent data structures to CL (but all the mutable stuff would still be available under the covers). I imagine that it'd be easier to add that stuff to CL than it would be to create a new language from scratch.
I honestly don't know how hard it would be to add software transactional memory and asynchronous agents to CL. I don't think it would be bad, but perhaps the end result would be so unlike CL that it wouldn't be pleasant to work in.
It's not so much that Common Lisp couldn't be implemented on the JVM and with transactional memory, it's that if one is going to do that much work, why not tune the language for that environment and for the programmers who live there? And once you do that, then picking a semantics for \.\ becomes a point of interest. Does one pick Common Lisp's or Java's usage for clarity , or the third option of complexity by overloading it?
There is the Common Lisp standard, and those variants really are managed like C compilers are. It's pretty great, and the differences are tiny.
Scheme has standards -- but yes, due to the language's easy implementation many people have made implementations of varying adherence to that standard. This is a good problem, it means people are able to implement their own languages.
Finally - clojure's rationale is pretty straight forward, and has obviously met an incredible niche in the PL space. I don't think it's that controversial of an implementation of S-exps.
I have always been a doubter of lisp likes and even I think this article makes no sense. If you are going to lump CL, Scheme, Clojure, etc together, then the list of "C" like languages is much much longer than the three they have in the article.
Because Lisp doesn't have a flat memory model, you can't directly produce a binary array suitable for direct copying to output device memory (ex. network card, GPU, etc) by simply expanding expressions using the built in syntax.
This means it's necessary for LISPers to continually reinvent the input-output layer and add non referentially transparent features to the language borrowed from Java or C in order to do any useful work.
Doing so is also contrary to the ideals of functional programming however, so they will continually overthrow the established LISP to invent a new one.
edit: to downvoters, replace "you" with "language runtime implementers", and understand that this is not a normative assertion, but a descriptive theory to explain why we have seen a huge number of non-interoperable Lisp\Scheme implementations with minor differences over the years.
> Because Lisp doesn't have a flat memory model, you can't directly produce a binary array suitable for direct copying to output device memory (ex. network card, GPU, etc) by simply expanding expressions using the built in syntax.
What are you talking about? Common Lisp has built-in support for vectors:
(let ((vector (make-array length :element-type 'unsigned-byte)))
;; do stuff with that flat array of bytes
;; …
(replace ext:gpu vector))
(In this example, EXT:GPU is a chunk of memory which is overwritten by VECTOR; one might conceivably use COPY-SEQ instead of MAKE-ARRAY, and one might of course just operate on EXT:GPU)
> Doing so is also contrary to the ideals of functional programming
Lisp isn't about functional programming; Lisp is a multi-paradigm language, supporting imperative, functional and OO programming out of the box (and can of course be extended to support aspect-oriented programming, table-oriented programming or what-have-you).
I would argue that a significant number of people were attracted to Lisp\Scheme variants over the years because of the promise of functional programming and the theoretical advantages of being to design programs using functional principles.
The problem with your example is that evaluation of the expression performs a side effect on an internal machine specific to the implementation of Common Lisp, and that it is not a portable and abstract expression defining a return value.
Ideally we would be able to define a return value as a variable length sequence of constituent elements through recursive evaluation, where the process of evaluation is guaranteed to compactly allocate all of the constituent elements contiguously on the same location of the stack.
This would allow the runtime implementation to directly copy the contents of a value of an arbitrary size to a necessary output device, so long as the runtime had access to a portable and functional definition of the value to be copied.
Because the Lisp language family does not provide for this, there has been a need to continually invent new implementation variants which each model different internal machines for performing these tasks.
But... Lisp was never (except for clojure) sold as a functional language ! The ml family is more inline with what you think lisp are. Lisp are multi paradigm and that's one of their strong point - with macro.
> The problem with your example is that evaluation of the expression performs a side effect on an internal machine specific to the implementation of Common Lisp, and that it is not a portable and abstract expression defining a return value.
I don't get what you're saying: REPLACE is bog-standard Common Lisp; it's not implementation-specific. You complained that it's not possible to produce a 'flat binary array suitable for direct copying to output device memory'; I demonstrated that it is.
> Ideally we would be able to define a return value as a variable length sequence of constituent elements
You can do that with '(simple-array unsigned-byte)…
Seriously, Common Lisp was built to be an industrial-strength language: it can bit-blit with the best of 'em, as well as supporting high-level dynamic concepts. It's pretty cool!
> complained that it's not possible to produce a 'flat binary array suitable for direct copying to output device memory'
I believe my mistake was using the term 'array' rather than 'string'.
This should read it's not possible to define a 'flat binary string, of variable length, in a pure functional manner, through implicit concatenation, with the guarantee that every return value reduces to a binary string, using the commonly accepted semantics of s-expression evaluation common to all Lisps'.
It certainly possible to build and process fixed size binary strings by adding, as I mentioned, "non referentially transparent features" to your language implementation, including Common Lisp's "replace" operation.
However, since such semantics are procedural and imperative in nature, they do not lend themselves well to the production of concise and declarative definitions suitable for describing the encoding of formatted binary strings adhering to a specific protocol, such as what one might find in the documentation of protocols for mass communication, encryption, and long term storage.
If they did, then we would most likely expect to see Scheme\Lisp\Clojure programmers of all varieties directly importing the same open protocol definitions from an established sources directly into their application codebases, and not repeatedly rewriting this functionality or relying on bindings to C\Java libraries.
> Common Lisp was built to be an industrial-strength language
I am not discussing the limitations of Common Lisp in particular, I am discussing the limitations of Lisp family of languages as a whole, and describing why there will most likely always be someone implementing a new slightly different variants of it, of which Common Lisp is but one example, and why it is unlikely that Lisp programmers will ever converge on one single language.
In what way? Either there is a need for variants, or there is no need for variants but it is perfectly okay to make them, or there is no need and it's not okay to make them.
There is a difference between language and execution. I can put together a clojure server and have it deployed virtually anywhere in about 10 minutes. I have never tried to deploy a CL app but I imagine it must be a little more complicated than making an Uberjar. The semantics of Lisp are beautiful. Why do we need to force them into a single language when it's best strength is portable symbolic logic?
I'm not Common Lisper, but actually CL distribution is trivial compared to JVM (for user applications, not servers). It's possible to create native executables, which don't need any frameworks to exist on the target systems. So deploying CL may take more work (making multiple executables), but the programs itself don't need users to have some frameworks installed before.
It's not a binary, either/or choice between "Lisp is perfect" and "we need a new variant of Lisp". Here's a map of possible attitudes toward Lisp. Take your pick.
On the Lisp-affinity axis,
1. Lisp is almost perfect as-is, we don't need new variants which will only adulterate the purity and greatness of Lisp as it already exists.
2. Lisp is great, and my favorite variant (e.g. Scheme) is the greatest variant because it is the purest available representation of the Lisp concept.
3. Lisp is mostly great, it just needs features x, y, z... which are provided by my favorite variant (e.g. Clojure).
4. Lisp is great, but really only for particular things, and its variants might be great for slightly different things.
5. Lisp isn't so great at all, and making new variants is just putting lipstick on a pig. Use a functional language other than Lisp (e.g. Haskell).
6. Functional programming as a whole sucks. Use a mixed-paradigm or imperative language (Java, C#, Perl, etc.)
7. High-level programming sucks. Use something without mandatory garbage collection (C++, Rust, C, Assembly).
On the intolerance-to-tolerance axis,
1. Lisp is almost perfect as-is, we don't need new variants which will only adulterate the purity and greatness of Lisp as it already exists.
2. Lisp is great as-is, so we don't really need new variants, but since they are also Lisp, they are also great.
3. Lisp is great, but some other languages are also great.
n. It doesn't matter what language you use, all language differences are only cosmetic.
This is more nomenclature than anything. We have a broad definition of what a lisp is, but a fairly narrow definition of what a C variant is. I'd argue that C++ and Java have more in common with each other than, say, Shen and Emacs Lisp.
Clojure isn't a minor variation on earlier Lisps, it's something that uses s-expressions with a significantly different set of default data structures, core library functions, and platforms.
This is like asking why we needed C when we already had ALGOL.
First point is true. Second point isn't: BCPL was created solely because they couldn't build an improved ALGOL (CDL) on a 1950's computer. Took out every good feature the hunk of junk couldn't compile. C was modified BCPL/B to do that on a PDP-11 w/ UNIX a watered-down MULTICS for same reason.
A comparison would be LISP 1.x vs modern Scheme or Common LISP. And only using LISP 1.x because you had to work with a microcontroller instead of a PC. ;)
The major contribution of Clojure is not that it's a Lisp; it's that its model of immutability-by-default eliminates whole categories of errors due to shared mutable state. I personally also enjoy having non-parenthesized literals for things like maps and sets, but those are incidental benefits.
> And why hasn’t the C world turned into a similar Tower of Babel?
Because it's much harder to produce a variant of C than it is to produce a variant of Lisp. Producing a variant of Lisp in Lisp (a.k.a. writing a macro) is a trivial exercise that anyone can do in a matter of minutes. Producing a variant of Lisp from scratch is an elementary exercise that an undergrad can do in a couple of hours. Producing a variant of C is an advanced exercise that would take even the most skilled programmer many days.
... Because so many languages are made in a matter of hours or days. Can we please drop the machismo? Making a language takes about a decade, with multiple people involved. A prototype one without a library can be made in a matter of months, but nobody would want to use those. I find it plausible that making a lisp is easier, especially if it's interpreted, but it's not that easy.
I think you misunderstood. A dialect of lisp is a fairly trivial exercise. We were able to do it in a single lecture back in college. Now that dialect had trivial and essentially useless functionality but allowed us to easily build on and create new languages, but lisp was designed to allow for these dialects to be made and to be made easily.
Did you make an interpreter in one lecture? Or did you just add a new macro to an existing language? S-expressions are tidy, tidy things, but just the underlying tech used to build a lexer, parser and interpreter takes a good while to understand, much less learn to write that quickly.
Lisper just reminded me of some HN commenter who once wrote that "any idiot can build Youtube, it's making it scale that's hard", and that guy gets on my nerves. Sorry about projecting that onto lisper.
There's tutorials to manually or automatically derive parsers for whatever you can clearly define. LISP's syntax makes this trivial. From there, you just need a few functions and tree operations for interpreting/compiling. LISP is designed for tree operations with tutorials and code for those. From there, a GC and I/O with many examples in papers and on the web with doc's plus code. Additionally, there's a ton of LISP/Scheme interpreters to draw on.
Hard to imagine an easier one to interpret. Compiling isn't that difficult if you target C as it basically takes two techniques. Takes a few more to target native and you don't get that in 90 minutes like one Scheme-to-C compiler presentation.
What is your counter-example in terms of mainstream languages that's easier to lex, parse, interpret, compile, or link than LISP? Or even as easy? Even Wirth's languages are more complex and difficult to compile despite being brilliantly simple.
Oh, I do agree Lisp is the simplest. No contest there. Hm, I guess it may be because building a new language (with some interesting properties) was my master thesis and it was really unpleasant. I guess I am reluctant to realize how much easier it would have been with a lisp.
That's a fair point. It's never easy and I had that rough experience, too. I mean, I wasn't a real expert on these things so mine was a reimplementation of existing work but still hard.
Curious, what was your language's interesting properties?
"I guess I am reluctant to realize how much easier it would have been with a lisp."
It might help if you see a modern example. The recent language impressing me the most with its features is Julia:
So, they appear to have built a simple LISP, then used it to incrementally build a compiler for a complex language. They just represent the syntax internally in a LISP form and work with it from there. Don't know much more than that but it shows the power of the concept.
Better demo is the one below as it goes step-by-step in stages. One commenter (Orion63) pointed out the author was re-using the proven cheat: "build a LISP, do it all in LISP, profit." Haha. I've considered duplicating that work with different language options.
My language's main feature was deterministic multithreading, of the synchronous school. It also forced a certain structure: All threads started and the shared variables they can write to have to be in the same area, arguments are always named, and sequence doesn't matter (optional in Python), shared variables can only be written to by one thread, but read by all. In contrast to some other deterministic multithreading solutions, the sequence in which IO is effected is also deterministic, not only the sequence of application to shared variables. The compiler had a typechecker and compiles to C++, using GCC to do the rest. I guess I should submit it to HN sometime...
That sounds interesting. Might be a decent stab at real-time programming and concurrency as it would need most of that.
"All threads started"
In many languages, the threads kind of sleep unless activated explicitly. Do you mean just the activated threads started or did you choose to have them all run at once for some reason?
"shared variables can only be written to by one thread, but read by all"
Good idea. BeOS also had read-many, write-one in form of benaphores: semaphores that only locked for writing. Strategy made it really fast.
"the sequence in which IO is effected is also deterministic"
What do you mean by that?
"compiles to C++"
The compile to C++ was my trick, too, since I knew I couldn't out-code GCC team. Although I wrote mine to avoid using C++ while being compatible lol...
"I guess I should submit it to HN sometime..."
You should. Either that or a forum where people discuss various language tradeoffs.
As a personal exercise, you should compare making a LISP interpreter or compiler to Wirth's treatment on doing an imperative language sometime. The difference is a 100-200 pages of work. Plus, the LISP can self-modify during compile to add any subsequent feature.
It's not machismo: it's just the design of LISP. Easy to parse, easy to modify, easy to evaluate, data as code, basic primitives that express anything else at least acceptably, and on 50's-60's hardware.
Additionally, building a useful LISP (esp Scheme) is an academic exercise done all over the world that's fairly easy even for amateurs. Building a useful, non-optimizing C++ compiler with only a subset of LISP capabilities is an exercise only undertaken by masochists very dedicated to compiler design. Even a prototype is easier for LISP (a few pages of code) while a prototype of C++ or even C's semantics is much more challenging. So, the empirical evidence counters your claim in terms of completed projects, their complexity, and features received in return.
It definitely doesn't take it months to walk away with a working implementation. Whether or not the language you've just written is actually useful or preferable to alternatives is a different question, and there I agree with you that communities, libraries, and language maturity can take a long time to build.
Lisp's simple syntax makes it a prime target for creating new dialects (like Clojure) to experiment with semantics.
I disagree there pretty heavily. There are a lot of C implementations out there -- gcc, clang, msc, icc, keil, cscript, tcc, pcc, etcetc. The big reason C hasn't turned into a similar Tower of Babel is that the various stakeholders in C recognize that it's a living language, and get together regularly to help work out methods that aren't useful anymore and add features to the specification to reflect what modern programmers want. If C had stopped standardization when ANSI C was released, you'd see a similar number of variants.
The prior point still stands if you compare the number of LISP modifications... basically every program with macros + interpreters + compilers... to the few things you've cited which were often a lot of work and hard to change. GCC particularly.
I agree the C crowd is more unified and that has huge impact. I'd go further to suggest that the legacy effect of UNIX and all the C libraries keep thing compatible. The C crowd is always sort of in maintenance mode for their language to keep everything working. While the LISP crowd rarely keeps anything and is often developing forms of the language. A subset are more practical with something long-term for real world use but most aren't. This difference between C use cases and LISP is worth exploring in answering these questions.
> I agree the C crowd is more unified and that has huge impact.
That's true, but it's important to understand which way the causality runs: the C crowd is unified because it's simply not possible to productive any other way. So C weeds out non-team-players in a way that Lisp doesn't.
That's a good point. Many suggested LISP needed that and would get more traction. Two example in that direction, Racket and Clojure, are kicking butt with one even mainstreaming. So, maybe major Schemes and CL's need to do that just a tad more.
I disagree there pretty heavily. There are a lot of C variants over there: Objective-C, C++, Pro*C, etc. (There are a lot of software projects that actually include a specific C pre-processor of some kind, check, eg. the C sources of emacs lisp, of GNU clisp, etc). As soon as you write a new front-end to C, you get a C variant, just like as soon as you write a DSL in Lisp you get a Lisp variant (and vice-versa).
The C world is a Tower of Babel.
Now, it's true that it's easier to do in lisp than in C, since in C the tools are external (you have to use flex/bison to implement the pre-processor) while in Lisp the tools are internal, and you just need to write a single defmacro to introduce your variant.
Producing a crappy interpreter for a toy C-like language is about the same as producing a crappy interpreter for a toy Lisp language.
C did turned into a Tower of Babel: in the libraries. For instance Win32 is essentially one language, POSIX another. Portability among Unix systems alone used to be a mess of #ifdef directives through the 1980's and 1990's.
>Producing a crappy interpreter for a toy C-like language is about the same as producing a crappy interpreter for a toy Lisp language.
Ab-initio perhaps (and even that isn't clear because parsing sexprs is a lot easier than parsing C). But producing a production-compiler for a non-toy Lisp-like language given a compiler for Lisp is a heck of a lot easier than producing a production-quality compiler for a non-toy C-like language given a compiler for C.
> C did turned into a Tower of Babel: in the libraries.
Sure, and for the same reason: it's easy to write library code in C. In fact, that's the only thing that's easy to do in C. So that's what C programmers do: they write library code. They don't, as a general rule, write compilers. So the compilers stay stable and the libraries don't.
Lisp is not a language like C (Common Lisp is, but Lisp is broader).
Lisp is a language family, sort of like you might have a family of languages with Algol-derived syntax.
And, you know, we see new members of the latter family all the time; they're just so dominant among programming languages that we don't even talk about them as a related group.
I think the question is framed poorly, and on that ground I'd challenge it from two angles:
First, one could argue that there are all sorts of additional C-like languages that weren't listed - Java, C#, Rust, Go, etc.
One could also argue that "Lisp" is a more fundamental category of languages than "C", so it's not an apples-to-apples comparison. Exhibit A: the author's list of Lisps included both dynamic and static languages, whereas the list of C's included only statically typed ones. It captures the idea of a functional language with a unified representation for both code and data. C-like, on the other hand, seems to specifically mean C and its superset languages (a much smaller family than its descendants) according to the author's usage.
Since Clojure is not a Lisp in the narrow sense, this is a useless question.
If Lisp means something broad, then we would list very different languages: Logo, Dylan, Julia, R, Javascript, Racket, ACL2, ... - again that would be a useless question.
Also, if GNU/Linux is so great, why do we keep needing new distributions... if Perl is so great, why do we keep needing new scripting languages? ... if the Porsche 911 is so great, why do we keep needing new variants?
78 comments
[ 3.6 ms ] story [ 143 ms ] threadAre they each better for some reason?
Why innovate on perfection? Or, why not improve the existing lisps rather than create new ones?
I understand what you meant, but I don't know the answer to these questions--before or after reading the article.
(I have given clojure a shot, though, and I enjoyed myself!)
I think the best way to look at this question is to think about who "we" is.
Are the sum total of programmers using lisp coordinating to make a final decision on how many lisps there should be? Certainly not. I don't know all the people using lisp, and I never will. So it doesn't make sense that they're trying to do anything. This community isn't defined in such a way that it's meaningful to talk about directed effort.
There are plenty of reasons to make different lisps without implicitly disparaging the functionality of existing lisps. Perhaps you want to learn about how lisps work by implementing your own. None of the existing languages would help you there. Perhaps you simply have a sense of curiosity about a language feature, and want to implement it somewhere. A less mature lisp might help, if the codebase is smaller and easier to reason about. Perhaps you need to use the JVM for business reasons. Perhaps the JVM is too much for the hardware you're running on, and need a C variant. Perhaps there's one with a particularly useful FFI to another language.
Those forms of excellence do not put Lisp on the JVM, nor give emphasis to multi-threaded, functional programming. Hence, Clojure.
"Customers and stakeholders have substantial investments in, and are comfortable with the performance, security and stability of, industry-standard platforms like the JVM. While Java developers may envy the succinctness, flexibility and productivity of dynamic languages, they have concerns about running on customer-approved infrastructure, access to their existing code base and libraries, and performance. In addition, they face ongoing problems dealing with concurrency using native threads and locking. Clojure is an effort in pragmatic dynamic language design in this context. It endeavors to be a general-purpose language suitable in those areas where Java is suitable. It reflects the reality that, for the concurrent programming future, pervasive, unmoderated mutation simply has to go.
Clojure meets its goals by: embracing an industry-standard, open platform - the JVM; modernizing a venerable language - Lisp; fostering functional programming with immutable persistent data structures; and providing built-in concurrency support via software transactional memory and asynchronous agents. The result is robust, practical, and fast."
via http://clojure.org/rationale
I honestly don't know how hard it would be to add software transactional memory and asynchronous agents to CL. I don't think it would be bad, but perhaps the end result would be so unlike CL that it wouldn't be pleasant to work in.
https://github.com/cosmos72/stmx
There is the Common Lisp standard, and those variants really are managed like C compilers are. It's pretty great, and the differences are tiny.
Scheme has standards -- but yes, due to the language's easy implementation many people have made implementations of varying adherence to that standard. This is a good problem, it means people are able to implement their own languages.
Finally - clojure's rationale is pretty straight forward, and has obviously met an incredible niche in the PL space. I don't think it's that controversial of an implementation of S-exps.
This means it's necessary for LISPers to continually reinvent the input-output layer and add non referentially transparent features to the language borrowed from Java or C in order to do any useful work.
Doing so is also contrary to the ideals of functional programming however, so they will continually overthrow the established LISP to invent a new one.
edit: to downvoters, replace "you" with "language runtime implementers", and understand that this is not a normative assertion, but a descriptive theory to explain why we have seen a huge number of non-interoperable Lisp\Scheme implementations with minor differences over the years.
What are you talking about? Common Lisp has built-in support for vectors:
(In this example, EXT:GPU is a chunk of memory which is overwritten by VECTOR; one might conceivably use COPY-SEQ instead of MAKE-ARRAY, and one might of course just operate on EXT:GPU)> Doing so is also contrary to the ideals of functional programming
Lisp isn't about functional programming; Lisp is a multi-paradigm language, supporting imperative, functional and OO programming out of the box (and can of course be extended to support aspect-oriented programming, table-oriented programming or what-have-you).
The problem with your example is that evaluation of the expression performs a side effect on an internal machine specific to the implementation of Common Lisp, and that it is not a portable and abstract expression defining a return value.
Ideally we would be able to define a return value as a variable length sequence of constituent elements through recursive evaluation, where the process of evaluation is guaranteed to compactly allocate all of the constituent elements contiguously on the same location of the stack.
This would allow the runtime implementation to directly copy the contents of a value of an arbitrary size to a necessary output device, so long as the runtime had access to a portable and functional definition of the value to be copied.
Because the Lisp language family does not provide for this, there has been a need to continually invent new implementation variants which each model different internal machines for performing these tasks.
I don't get what you're saying: REPLACE is bog-standard Common Lisp; it's not implementation-specific. You complained that it's not possible to produce a 'flat binary array suitable for direct copying to output device memory'; I demonstrated that it is.
> Ideally we would be able to define a return value as a variable length sequence of constituent elements
You can do that with '(simple-array unsigned-byte)…
Seriously, Common Lisp was built to be an industrial-strength language: it can bit-blit with the best of 'em, as well as supporting high-level dynamic concepts. It's pretty cool!
I believe my mistake was using the term 'array' rather than 'string'.
This should read it's not possible to define a 'flat binary string, of variable length, in a pure functional manner, through implicit concatenation, with the guarantee that every return value reduces to a binary string, using the commonly accepted semantics of s-expression evaluation common to all Lisps'.
It certainly possible to build and process fixed size binary strings by adding, as I mentioned, "non referentially transparent features" to your language implementation, including Common Lisp's "replace" operation.
However, since such semantics are procedural and imperative in nature, they do not lend themselves well to the production of concise and declarative definitions suitable for describing the encoding of formatted binary strings adhering to a specific protocol, such as what one might find in the documentation of protocols for mass communication, encryption, and long term storage.
If they did, then we would most likely expect to see Scheme\Lisp\Clojure programmers of all varieties directly importing the same open protocol definitions from an established sources directly into their application codebases, and not repeatedly rewriting this functionality or relying on bindings to C\Java libraries.
> Common Lisp was built to be an industrial-strength language
I am not discussing the limitations of Common Lisp in particular, I am discussing the limitations of Lisp family of languages as a whole, and describing why there will most likely always be someone implementing a new slightly different variants of it, of which Common Lisp is but one example, and why it is unlikely that Lisp programmers will ever converge on one single language.
I guess it seems like a non-issue to me.
Then again, applying my own reasoning:
There is no need to ask this question, but it's fine to ask it anyways.
On the Lisp-affinity axis,
1. Lisp is almost perfect as-is, we don't need new variants which will only adulterate the purity and greatness of Lisp as it already exists.
2. Lisp is great, and my favorite variant (e.g. Scheme) is the greatest variant because it is the purest available representation of the Lisp concept.
3. Lisp is mostly great, it just needs features x, y, z... which are provided by my favorite variant (e.g. Clojure).
4. Lisp is great, but really only for particular things, and its variants might be great for slightly different things.
5. Lisp isn't so great at all, and making new variants is just putting lipstick on a pig. Use a functional language other than Lisp (e.g. Haskell).
6. Functional programming as a whole sucks. Use a mixed-paradigm or imperative language (Java, C#, Perl, etc.)
7. High-level programming sucks. Use something without mandatory garbage collection (C++, Rust, C, Assembly).
On the intolerance-to-tolerance axis,
1. Lisp is almost perfect as-is, we don't need new variants which will only adulterate the purity and greatness of Lisp as it already exists.
2. Lisp is great as-is, so we don't really need new variants, but since they are also Lisp, they are also great.
3. Lisp is great, but some other languages are also great.
n. It doesn't matter what language you use, all language differences are only cosmetic.
This is like asking why we needed C when we already had ALGOL.
A comparison would be LISP 1.x vs modern Scheme or Common LISP. And only using LISP 1.x because you had to work with a microcontroller instead of a PC. ;)
Because it's much harder to produce a variant of C than it is to produce a variant of Lisp. Producing a variant of Lisp in Lisp (a.k.a. writing a macro) is a trivial exercise that anyone can do in a matter of minutes. Producing a variant of Lisp from scratch is an elementary exercise that an undergrad can do in a couple of hours. Producing a variant of C is an advanced exercise that would take even the most skilled programmer many days.
Lisper just reminded me of some HN commenter who once wrote that "any idiot can build Youtube, it's making it scale that's hard", and that guy gets on my nerves. Sorry about projecting that onto lisper.
Hard to imagine an easier one to interpret. Compiling isn't that difficult if you target C as it basically takes two techniques. Takes a few more to target native and you don't get that in 90 minutes like one Scheme-to-C compiler presentation.
What is your counter-example in terms of mainstream languages that's easier to lex, parse, interpret, compile, or link than LISP? Or even as easy? Even Wirth's languages are more complex and difficult to compile despite being brilliantly simple.
Curious, what was your language's interesting properties?
"I guess I am reluctant to realize how much easier it would have been with a lisp."
It might help if you see a modern example. The recent language impressing me the most with its features is Julia:
http://julialang.org/
Wondering aloud about how they pulled all that off, esp macro's, led someone here to tell me it's actually femtolisp internally:
https://github.com/JeffBezanson/femtolisp
So, they appear to have built a simple LISP, then used it to incrementally build a compiler for a complex language. They just represent the syntax internally in a LISP form and work with it from there. Don't know much more than that but it shows the power of the concept.
Better demo is the one below as it goes step-by-step in stages. One commenter (Orion63) pointed out the author was re-using the proven cheat: "build a LISP, do it all in LISP, profit." Haha. I've considered duplicating that work with different language options.
https://news.ycombinator.com/item?id=9699065
"All threads started"
In many languages, the threads kind of sleep unless activated explicitly. Do you mean just the activated threads started or did you choose to have them all run at once for some reason?
"shared variables can only be written to by one thread, but read by all"
Good idea. BeOS also had read-many, write-one in form of benaphores: semaphores that only locked for writing. Strategy made it really fast.
"the sequence in which IO is effected is also deterministic"
What do you mean by that?
"compiles to C++"
The compile to C++ was my trick, too, since I knew I couldn't out-code GCC team. Although I wrote mine to avoid using C++ while being compatible lol...
"I guess I should submit it to HN sometime..."
You should. Either that or a forum where people discuss various language tradeoffs.
It's not machismo: it's just the design of LISP. Easy to parse, easy to modify, easy to evaluate, data as code, basic primitives that express anything else at least acceptably, and on 50's-60's hardware.
Additionally, building a useful LISP (esp Scheme) is an academic exercise done all over the world that's fairly easy even for amateurs. Building a useful, non-optimizing C++ compiler with only a subset of LISP capabilities is an exercise only undertaken by masochists very dedicated to compiler design. Even a prototype is easier for LISP (a few pages of code) while a prototype of C++ or even C's semantics is much more challenging. So, the empirical evidence counters your claim in terms of completed projects, their complexity, and features received in return.
As an example, here's a guide for writing a Scheme interpreter in a matter of hours: https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_...
It definitely doesn't take it months to walk away with a working implementation. Whether or not the language you've just written is actually useful or preferable to alternatives is a different question, and there I agree with you that communities, libraries, and language maturity can take a long time to build.
Lisp's simple syntax makes it a prime target for creating new dialects (like Clojure) to experiment with semantics.
I agree the C crowd is more unified and that has huge impact. I'd go further to suggest that the legacy effect of UNIX and all the C libraries keep thing compatible. The C crowd is always sort of in maintenance mode for their language to keep everything working. While the LISP crowd rarely keeps anything and is often developing forms of the language. A subset are more practical with something long-term for real world use but most aren't. This difference between C use cases and LISP is worth exploring in answering these questions.
That's true, but it's important to understand which way the causality runs: the C crowd is unified because it's simply not possible to productive any other way. So C weeds out non-team-players in a way that Lisp doesn't.
The C world is a Tower of Babel.
Now, it's true that it's easier to do in lisp than in C, since in C the tools are external (you have to use flex/bison to implement the pre-processor) while in Lisp the tools are internal, and you just need to write a single defmacro to introduce your variant.
C did turned into a Tower of Babel: in the libraries. For instance Win32 is essentially one language, POSIX another. Portability among Unix systems alone used to be a mess of #ifdef directives through the 1980's and 1990's.
Ab-initio perhaps (and even that isn't clear because parsing sexprs is a lot easier than parsing C). But producing a production-compiler for a non-toy Lisp-like language given a compiler for Lisp is a heck of a lot easier than producing a production-quality compiler for a non-toy C-like language given a compiler for C.
> C did turned into a Tower of Babel: in the libraries.
Sure, and for the same reason: it's easy to write library code in C. In fact, that's the only thing that's easy to do in C. So that's what C programmers do: they write library code. They don't, as a general rule, write compilers. So the compilers stay stable and the libraries don't.
Besides, Clojure has some fairly modern and novel stuff going on when it comes to concurrency, in addition to being a wholly modern Lisp.
Lisp is a language family, sort of like you might have a family of languages with Algol-derived syntax.
And, you know, we see new members of the latter family all the time; they're just so dominant among programming languages that we don't even talk about them as a related group.
First, one could argue that there are all sorts of additional C-like languages that weren't listed - Java, C#, Rust, Go, etc.
One could also argue that "Lisp" is a more fundamental category of languages than "C", so it's not an apples-to-apples comparison. Exhibit A: the author's list of Lisps included both dynamic and static languages, whereas the list of C's included only statically typed ones. It captures the idea of a functional language with a unified representation for both code and data. C-like, on the other hand, seems to specifically mean C and its superset languages (a much smaller family than its descendants) according to the author's usage.
If Lisp means something broad, then we would list very different languages: Logo, Dylan, Julia, R, Javascript, Racket, ACL2, ... - again that would be a useless question.
Also, if GNU/Linux is so great, why do we keep needing new distributions... if Perl is so great, why do we keep needing new scripting languages? ... if the Porsche 911 is so great, why do we keep needing new variants?
from http://blog.samibadawi.com/2013/05/lisp-prolog-and-evolution...