Lispers
lispers.org
lispers.org
And I won't say that the paradigm shift to functional programming didn't broaden my view either, my point is that there is more to Lisp than being functional-ish.
When doing theorem proving in Ocaml (which was both ML's and Lisp's MOs), my REPL session could last months. I sadly didn't have Lisp's great (save-lisp-and-die) in Ocaml, but I was able to snapshot the process state to get semiway there.
Common Lisp is built around the idea that your runtime is a persistent live system that you stay in and modify and introspect. Your tool-chain should consist entirely of Lisp objects that are part of your runtime, and should themselves be modifiable. Everything is programmable. The Art of the Metaobject Protocol is still one of the coolest things I've ever read.
I'm a Haskeller now, through and through, but Haskell's REPL and runtime isn't comparable to Lisp's.
It also gets unwieldy when programs get complex.
[1] http://compgroups.net/comp.lang.lisp/static-type-checking-in....
There are lot of schools and if you are "be a cool web app developer"-school where you learning direct skills reauired most common web dev jobs today... Yeah lisp is not essential. But if you are "computer SCIENCE"-school and you targeting variety of fundamental cocepts of field I say that lisp is quite essential.
I'm sure you've nothing to do with compsci because lisp is a useless language family. If anything is recommended than it's rust, c and idris.
Scheme is particularly suited to parsing, interpreting and compiling.[0][1] There's a reason GCC's internals often look like Lisp.
If you exclude language design from computer science, then maybe Lisp becomes a "useless language family", but there's a reason MIT Scheme was created.
> There's a reason GCC's internals often look like Lisp.
Dude, that notation is not original to lisp...
So first you say Lisp is useless in compsci because of the existence of "rust, c and idris", and now its pointless to use it because of ML?
> Dude, that notation is not original to lisp...
Firstly: Have you actually looked at an MD file? [0] RTL is a Lisp dialect.
Secondly: The first paper on S-Expressions appeared in November, 1997. McCarthy's first paper on what would become Lisp in 1982. I'm aware of no language, and can find reference to no language, that used that notation before Lisp did, so yes it is original to Lisp.
[0] https://gcc.gnu.org/git/?p=gcc.git;a=blob_plain;f=gcc/config...
Idris and Rust are ML descendants and C helps you to learn the basics of low-level programming. Lisp on the other hand won't teach you anything. How hard is this to understand?
> Secondly: The first paper on S-Expressions appeared in November, 1997.
That was just a draft of an RFC which never got approved. S-Expressions are MUCH older - it's "Polish Notation" invented by Jan Łukasiewicz in 1924
> McCarthy's first paper on what would become Lisp in 1982.
Lisp was specified in 1958.
> I'm aware of no language, and can find reference to no language, that used that notation before Lisp did, so yes it is original to Lisp.
1. It's not about who used that notation but who invented it - not McCarthy; 2. that notation is practically useless for programming languages but they're good for ASTs.
Difficult when the definitive compsci course, Structure and Interpretation of Computer Programs starts, and ends, with Lisp.
> S-Expressions are MUCH older - it's "Polish Notation" invented by Jan Łukasiewicz in 1924
No. Polish notation, as described by Łukasiewicz, is:
> I came upon the idea of a parenthesis-free notation in 1924
Polish notation has a strict set of precedence rules, and no parentheses. At all.
S-Expressions contain Polish notation, but is a superset that allows you to specify arguments in a less painful precedence than Polish notation, and with variadic arguments, which is not possible in Polish notation.
> that notation is practically useless for programming languages but they're good for ASTs.
Several decades of computer science disagree with you.
A dialect like Common Lisp was designed such that applications in the large are possible. As any large program it needs a bit thought how to make a scalable architecture and how to best use and extend the facilities provided by rhe development environment and the language.
Interesting. Can you mention some of the Lisp language or other features that help with that?
* namespaces for symbols, in earlier days Lisp had a flat namespace
* compilation of files, means in a large system you can use 'ahead of time' compilation, where the compiler might give a lot of diagnostic messages. There is very little penalty to that, basically one can compile always and interactively.
* type declarations/hints which can be used for optimization, type checks -> see SBCL and CMUCL, and documentation
* lots of declaration forms which can be checked at compile time or runtime
* macros which can be expanded at compile-time. This can create early warnings/errors.
* function arglists, where the usage can be checked at compile-time (missing arguments, added arguments, ...)
* keyword arguments, which makes code more readable
* a very elaborate error handling system, which can not only catch errors, but which can be used to interactively or automatically repair some errors. Thus you don't get 'program crashed', but a handler gets called in the error context, which than can use provided explicit restarts. Errors types can be organized in an ontology with inheritance.
* runtime assertions and type checks work with the error system
* documentation features included: many declarations allow documentation strings
* robust runtimes which by default prevent corrupting the running Lisp as much as possible. Some earlier Lisps created unsafe compiled code. By default I would expect a Common Lisp system to detect all kinds of wrong access to data structures at runtime.
* optimizations, which can be restricted to certain code blocks
Plus
* an object system which can be used to provide extensible software, typically used to structure larger software. Also provides multi-methods which prevent being called with arguments of wrong types at runtime. Multiple inheritance. This allows a lot explicit structure in larger applications, which also can explored by reflection and which can be tailored for specific needs.
* features like macros and reader macros can increase the code density in larger software -> fewer lines of code and domain level code. By default Common Lisp favors larger identifies, but in larger codebases macros can significantly reduce code size and make external tools like preprocessors obsolete.
A different development methodology:
In static languages you tend to statically compile a program, build it, start it, test it. May be in some debug mode or with some kind of extension language interface.
In Common Lisp one may develop with a build while it is running and then make sure that these builds can be reproduced by compiling/loading the whole thing. This can reduce the typical development cycle time drastically. But different development models are possible: fully batch, pre-compiled + interactive, fully interactive.
It also demonstrates that Scheme is flexible enough to easily implement features that the language designers did not, such as monads and laziness. While both can be done in almost any language, I think that Scheme's macros allow for exceptionally seamless implementations.
I'm trying to look up what this means. Do you have a link?
That's not the only instance of "staged" execution. Guix introduces G-expressions, a quoted expression in a build context where unquoted package values are replaced with absolute directory names that are not known until execution time.
Quoting and unquoting code and dealing with different strata of evaluation come natural in Scheme.
Another simpler example of staged execution might be the remote code execution feature of Guile-SSH.
You may also like to read about multitier programming in Hop: http://queue.acm.org/detail.cfm?id=2330089
Axiom is a FOSS computer algebra system implemented in about 1.2 million lines of Common Lisp.
Axiom relies heavily on the ability to dynamically define and re-define functions. It relies heavily on the macro facility. It presents a domain specific language (called SPAD) which implements mathematically friendly syntax and semantics. (https://github.com/daly/axiom)
Oh I like this one (emphasis mine) :)
Many of the quotes makes me question the authors experience. Everything is relative. Compared to Fortran, Lisp is all that and more. Compared to modern languages (say, Haskell), not so much.
Around 1987 I was as a student sitting in front of a real Lisp Machine, a Symbolics 3600, running in an AI research lab for natural language interaction. The machine with a stack-based CPU ran an object-oriented operating system written in a million lines of Lisp, with full source code and runtime changes.
There is nothing which will blow your mind like that.
McClim is not a business case. It is just a project to revive the most sophisticated GUI for Lisp. The graphical shell [1] for instance which already works gives a remarkable impression.
[1] https://common-lisp.net/project/mcclim/static/media/screensh...
Do you have enough experience that you dare to judge him?
> Compared to modern languages (say, Haskell), not so much.
I disagree. I was in a lot of languages, in all different programming styles. Lisp still is the language with a maximum of freedom of programming, probably also regarding productivity. Only Nim comes close.
Haskell may look elegant at the surface, regarding productivity however I don't recommend it except for DSL and compiler construction. If you are not a world class expert in Haskell then in most cases Haskell takes a lot more time for development than other languages (say Lisp, Python, C++, even Rust).
- and takes negligible maintenance/refactoring time compared to Lisp/Python/C++. Maintenance timesink might easily outweigh the gain in development time.
That's the promise of strong type systems: preventing bugs from happening before you have to debug them in production and it does not come absolutely free.
I actually had heard something similar about how JPL used Lisp and they could just debug the satellite remotely from the earth.
In something like Common Lisp you can do changes to the object-system at runtime, both to the program and the programming language. For example you can load a patch, which changes the class hierarchy, adds slots, ... Also existing objects can be changed - they can change their class or the list of slots, etc.
That is correct. However, in Haskell you need a lot of discipline to guide a big project in the right direction. Maintenance can quickly became a nightmare due to very dense code.
If maintenance is the main issue I would choose Ada than Haskell. I have no problem to understand my own Ada code written 10 years ago, even without documentation. In Haskell however I had problems to understand my own code which was just some weeks old :-)
Have you worked with Haskell professionally? Your observations do not match my experience of approx. 3 years of professional Haskell development across 3 different companies. Guiding the growth of Haskell codebases is trivial compared to Python, which is the other language I have professional experience of.
Not yet. I was seriously interested in Haskell. The cabal hell however put me back. Stack is interesting but installation from source is still troublesome.
I always want to be able to install my developer tools from scratch (by source only) since I want to make sure that I can port my software to new systems. Haskell's and Rust's way of installation (curl | sh) is convenient but inacceptable for porting. Nim's bootstrap from source (no dependencies except a C compiler) is the right way how it should be.
Doing anything more AIish will need an old-fashioned symbolic system and that stuff is still hard. Take for example IBM's Watson. It's a big system with all kinds of different modules. The language frontend is written in old-fashioned Prolog.
I don't have the latest edition, but from this appendix http://aima.cs.berkeley.edu/newchapb.pdf I understand it's in pseudocode. And there is a python implementation online, but it's not the only one.
It's like "don't bother with differential equations; math is more into elliptic curves now".
The symbolic computation and its representations of knowledge and pattern matching and so on do things that number-crunching networks over big data simply don't.
Like for instance, get certain jobs done with little data: a compact encoding of specific knowledge.
On the other hand, the spirit of Lisp has successfully taken over the world. The modern Lisp is called JavaScript.
I am perfectly aware of all the differences between Lisp and JavaScript and of the other languages that have influenced JavaScript (Self, Perl, Lua), but think of this: what made Lisp so great in the past?
- automatic memory management (compared to Fortran/C);
- very dynamic language and environment,
hooks on everything, live systems;
`eval`, available meta-interpreter;
- exceptions and condition system instead of segfaults;
- multi-paradigm, flexible programming language:
functional, OO and imperative programming when needed;
- research roots and great minds behind the language;
What JavaScript has fixed compared to Lisps?JavaScript has fixed S-expressions; "I can program in S-expressions" does not mean "I should". Macros are harmful to programming in the large.
JavaScript applied existing lessons about data structures and object systems to the language: "I can roll out my own object system" does not mean "I should" and creates a Babel tower of incompatible object systems instead of a common language. JavaScript got rid of linked lists (that are awkward and impractical for everyday use) and introduced hash-tables into the language.
The modern JS ecosystem seems to be exactly what a Common Lisp ecosystem would have been if it had existed in the first place: the same diversity, the same problems, the same churn rate (because the language is powerful and writing new stuff is easy).
JavaScript has also successfully escaped perception of "a language for brains". Research background is actually harmful for a modern programming language. Examples are abound: Haskell is widely derided by the industry, slightly researchy Scala is "borderline unmanageable" for many and teams move back to Java; C++ with its experiments is tolerated for its interoperability with C, but gets a lot of (deserved) hate for its (often unnecessary) complication (hopefully, Rust has a good cover for the industry as "enhanced C++"); simplistic Go takes the world over by storm; the web is mostly written in PHP, whose inventors are as far from PL research as one can get.
Generally I agree that Javascript is similar, in many ways. But worse. Worse can be better, but we know that already. ;-)
(Anyway, I would agree with what I think is your actual position that it's more than just language tools; not arguing...).
Those who consider javascript a good language usually lack the experience to compare languages effectively. Javascript is mostly appealing to front-end developers because they usually don't know anything else. Also, there are a LOT of front-enders so, you can explain the "popularity" with that. JS has many warts but js-zealots will just ignore it due to the hype.
> simplistic Go takes the world over by storm;
Proof on that? Because so far, the only ones using it are the ones who just don't know how to use other languages. The y prefer garbage code over learning generics...
> the web is mostly written in PHP, whose inventors are as far from PL research as one can get.
Proof on that "mostly" again? Also, most of those sites are unstable and known to be really hard to maintain. PHP was just an easy entry for unskilled labour which is a bad thing since they'll create worse products which will cost more in the long run. But beginners don't care about the long run - they just want the money from ads.
> I am perfectly aware of all the differences between Lisp and JavaScript and of the other languages that have influenced JavaScript (Self, Perl, Lua), but think of this: what made Lisp so great in the past?
Your points are too subjective:
> automatic memory management (compared to Fortran/C)
Did lisps took part in researching modern GCs? I don't think so. I thought it only had a simplisitc ARC only...
> very dynamic language and environment,
And it's a very useless feature - or prove it otherwise. Strongly and statically typed languages can `eval` too.
> multi-paradigm, flexible programming language: functional, OO and imperative programming when needed;
1. First lisps wasn't OO and modern lisps are either NOT OO or support it poorly; 2. imperative programming is not that comfortable in lisp; 3. lisp is not a good FP language: just compare it to ML.
> research roots and great minds behind the language;
Or more like failed experiments and like-minded enterpreneurs.
Javascript IS a powerful language with features that were not in other mainstream language for decades and some still don't have: first class functions, dynamic typing, lambda functions, closures etc. You only have to look at the diverse uses of Javascript to appreciate how far that power goes.
As for the warts of JS any experienced Javascript programmer has learned to work around those and it takes a small effort to learn how to do so.
> Proof on that "mostly" again?
You really need proof that the majority of the web is written in PHP?
As for an entry point for unskilled labour, my first PHP code was written after I'd been a C++ programmer for 20 years. I found it similar to Javascript; some warts to be aware of but otherwise a powerful language it's possible to be very productive in in a short amount of time. I refer you to the look at the many Facebook tech blog posts on the topic.
Whilst PHP has some limitations around it's runtime model, if you can stick within it the modern PHP is a very good tool for backend development.
> modern lisps are either NOT OO or support it poorly
Have you read up on CLOS? It goes way beyond the features of, say, C++ object model. By limiting yourself to a subset you can have the basic OOP features; inheritance, polymorphism and encapsulation. It provides more and arguably non-oop features such as multiple dispatch, methods that can run before, after and around your own methods (aspect oriented in modern parlance).
There is an excellent book by Sonya Keene on CLOS and OOP in Common Lisp.
This is ridiculous and it just shows how you don't have any experience with ML languages.
> As for the warts of JS any experienced Javascript programmer has learned to work around those and it takes a small effort to learn how to do so.
Again, ridiculous excuses.
> You really need proof that the majority of the web is written in PHP?
Yes.
> As for an entry point for unskilled labour, my first PHP code was written after I'd been a C++ programmer for 20 years. I found it similar to Javascript; some warts to be aware of but otherwise a powerful language it's possible to be very productive in in a short amount of time. I refer you to the look at the many Facebook tech blog posts on the topic.
It's not powerful, you just don't have experience with effective languages.
> Have you read up on CLOS? It goes way beyond the features of, say, C++ object model. By limiting yourself to a subset you can have the basic OOP features; inheritance, polymorphism and encapsulation. It provides more and arguably non-oop features such as multiple dispatch, methods that can run before, after and around your own methods (aspect oriented in modern parlance).
That was added after and still looks bad overall.
Perl, Ruby, Python say hi.
You are trolling.
C wasn't even a thing when Lisp systems happened and they were running on computers that were quite humble when compared with PDP-11.
When will JavaScript AOT compile to native code, or provide primitives for systems programming like Lisp?
http://www.randomhacks.net/2005/12/03/why-ruby-is-an-accepta...
Ruby blocks cover a lot of what Lisp macros do. I don't know much Python and JS, but I'm guessing those don't have something similar to blocks/macros.
That's a misconception. Ruby blocks are mostly unrelated to macro usage.
Replacing them with a semicolon and curly brace riddled abomination; see CoffeScript and various syntax extensions that come with latest JS standards.
Also note how many years it took for JS to get even very basic features, like `() => {}` lambda notationor multiline strings. In a language with a macro system you can implement both easily.
> Macros are harmful to programming in the large.
This is not true. Macros are mainly used to implement DSLs, and well designed DSL can shorten the code by an order of magnitude. Of course, when to create one is hard to say, but not harder than when to write a metaclass in Python. Would you advocate removing metaclasses from Python?
When not for DSLs, macros are used as compile-time functions. You can easily write such macros in any language, provided you split your program in two parts: preprocessor (which parses and transform the code) and the code proper. That's a lot of work, which disappears with the introduction of a macro system. I don't think this has any impact on "programming in the large".
Many "modern" and "systems" programming language feature macro systems. Haxe, Nim, Rust, Julia spring to mind immediately, followed by OCaml, Haskell, Elixir, Erlang and quite a few more.
In other words: if you want to say that "macros are harmful" in some way you're going to need to provide some kind of argument and evidence.
> creates a Babel tower of incompatible object systems instead of a common language.
Three of the most popular Lisps - Racket, Clojure and Common Lisp - have an object system in the language. EDIT: Emacs Lisp might also qualify, but I don't remember if EIEIO is included by default in Emacs.
> JavaScript got rid of linked lists (that are awkward and impractical for everyday use)
Many languages outside of Lisps seem to disagree: Elixir and Erlang, Haskell, OCaml and Prolog, for instance. And btw: I disagree with this too.
Linked lists are a very convenient and surprisingly universal data structure, but they really shine wherever you have a recursive algorithm. Which is, in the languages mentioned, almost all the time.
> and introduced hash-tables into the language.
Clojure, Common Lisp, Racket, Emacs Lisp and probably others provide a rich set of data structures, including vectors, bitsets, hashes, queues, sequences and more.
Linked lists were way ahead of their time. Nowadays everything is so fast that we can probably use them without guilt, but there is still a sense of disgust associated them.
However, linked lists offer many advantages that arrays do not. The length of a list need not be known at compile time, for one. It's trivial and very fast to insert a new element in the middle of the list. They are not optimized for random access, but they pose no performance penalty (over arrays) if you need to traverse them from start to end.
But the most important advantage of linked lists is that they are their own iterators (in C++ parlance). You don't need an additional state for iterating over a list - you don't need to store and update the index, for example. Each element of a list is the beginning of the rest of the list, so if you hold a reference to one, you can stop and restart iteration without problems. Moreover, you can add elements to the list and still be able to use the previously captured element as a start of new iteration: no need to invalidate the iterator.
In other words, Linked List has some very desirable properties and it makes sense to use it for almost anything other than a fixed-length, random-access collection. All Lisps provide vectors and hashes for the latter use-case.
I think the problem with Linked Lists is that they are not hard to implement and taught on the entry-level courses, with C as an implementation language. This makes it very hard to see their usefulness. With a bit of tooling (library functions) and language support, they really shine.
It was done on some Lisp Machines with CDR-coding. newly allocated lists did not have any CDR pointers. Optimizing lists that way could also be done by the garbage collector, when it needed to copy lists or in optimization runs before saving an image.
Most other implementations did not implement it.
That, and npm is a horror movie.
Please tell me how to get a reasonable implementation of list or tree values in JavaScript, without resorting to Gödel encodings or gentlemen agreements not to query object identities.
Now recall that functions are mappings from values to values. How are we supposed to express computation as function evaluation without a rich set of values to work with?
That's how well JavaScript (or Lisp, for that matter) supports functional programming. (I can't comment on object orientation support because I don't know of a logically consistent theory of objects.)
> JavaScript has fixed S-expressions
Weird. Lisps are traditionally semantically a mess (Scheme less so than Common Lisp, though), but the syntax, allowing for relatively easy metaprogramming, is their main saving grace.
> Macros are harmful to programming in the large.
Just because you don't understand macros, it doesn't mean other people can't put them to good use.
> JavaScript got rid of linked lists (that are awkward and impractical for everyday use) and introduced hash-tables into the language.
Practical Lisps provide several useful data structures out of the box. In any case, this is a matter of standard library design, not language design.