Lisp Quickstart
cs.gmu.edu
cs.gmu.edu
Before anyone takes potshots, please understand that these tutorials have a very specific purpose: to get our AI students up and coding AI projects as quickly as possible given what they know (C++/Java). So instead of talking up front about the things that make Lisp special, I'm basically talking about how to write C in Lisp. Hence, the tutorials don't ever discuss CLOS, macros, advanced packages, conditions, streams, the type lattice, SLIME, ASDF, Quicklisp, indeed any installable packages at all. Because we get to many of those things, and the wonders of Lisp, later in the class.
It is a quick reference guide consisting on a set of pages that can be stacked together to form a little booklet. Beautiful to look at.
I wish I had this for other languages and cannot recommend it enough.
Thanks for sharing.
https://en.wikipedia.org/wiki/Syntax_diagram
If we google for "syntax diagram" or "railroad diagram" there are better examples, like this one: https://i.stack.imgur.com/uDoc4.gif
We see all the "railroad paths" through the syntactic combinations. Because it's not a recursive grammar, it's practical to unfold everything in one diagram. Instead of bubbles and arrows, he marked it up with LaTeX nested parentheses in 2D.
The whole guide is somewhat similar in flavor to the cheat sheets in mathematics books (trig identities, derivatives, integrals, useful equations, and so on).
Not true anymore. This article seems rather old, considering it refers to Racket as "PLT Scheme" here: http://cs.gmu.edu/~sean/lisp/, so it's at least 7 years old, possibly older.
You said it yourself, if you specify generic close by value, you will get the behavior you want. If you know your closure won't outlive the stuff it closes over, you can capture by ref. If you need some other behavior, you use smart pointers and capture by value or mix and match based on what you're capturing.
Just because C++ doesn't automatically figure it out for you isn't an argument against it having closures, any more than not a having bignum or rational built in means it can't do arithmetic.
Or not.
One approach to development in CL is to evaluate a form you would like to have. You end up in the debugger. You write the implementation until the error goes away. Repeat until you have a working system... interactive, hate-driven development at its finest.
In fact that's one of my favourite things about CL: it's so interactive that it almost never crashes. You can still interact with your program, see all of the data, debug it, and continue.
I learnt game programming and OpenGL from the ground up using CL. If it were not for CL, I probably would not have the patience to hang around.
I used LispWorks OpenGL bindings and with vertical sync integration, live updates to a running OpenGL program after modifying the code is/was super fun.
The biggest advantage I have found using CL is in learning hard topics as you at least have the language and the tooling get out of your way helping you focus on the problem at hand.
That's radically different from Smalltalk and the Dynabook. Part of its mandate was to be used by children which by definition meant there was an assumption of substantial difference in cognitive capacity between end users and programmers.
That's the intellectual model that's mostly won. It's distinct from a model in which the causal premise for differences in knowledge is expertise and experience.
http://www.codersnotes.com/notes/a-constructive-look-at-temp...
This doesn't sound very sustainable or effective to me... almost all the bugs I encounter in programming are algorithmic in nature. The code compiles without error and runs without crashing, but it just happens to do X instead of Y. The code fails because I didn't understand the problem well enough. That's one thing that computers can't really help with yet.
Note that this is not how I really program either, but I've not tried it enough to have an opinion about whether it's effective for me, and I suspect it'll be one of those 'matter of taste' things (I barely use interactive debuggers at all).
If HN had a FAQ this might be near the top. As new people go into CS they seem to be aesthetically drawn to, or repulsed by, Lisp. The irony is there is no objective truth so these two groups of people spend a lot of time supporting their arguments (google search will amaze you, even Paul Graham has been sucked into this) but it's not very common for people to change sides after their initial indoctrination.
I have come to view this as one of few (only?) metaphysical topics seriously discussed in computer science.
Someone on SO once said: 'the correct Lisp answer is more gnomic. Something like: "If you have to ask, you are not ready." Then if anyone questions further, the correct answer is either "yes" if it's an either/or question or "You are not ready."'
I don't know how valid this is, since I've only spoken with a handful of Lispers, but it seems that they appreciate the freedom and lack of boundaries/rules/limits that Lisp provides. They talk about it like a language that can do anything, because it has no rules on what it can or cannot do.
But at the same time, my friends also remind me that the lack of limits means that they are more accountable for every line of code, and that as a beginner, it'd be best for me to use something more structured (they always say Python) until I'm comfortable taking off the training wheels.
Can anyone share their thoughts?
Here are some probably not very contentions examples:
If you pick Brainfuck for your language it will probably take you longer to write your program than Java.
You will probably be able to write a network server in Go faster than you will be able to write it in assembly.
Those may seem contrived, but if you start to compare things that could be actual business decisions it becomes much harder for everybody to remain objective.
You also get to the point for which there may be no universal answer for each programmer. This is at least partly because many programming tasks are quite mundane, and so wouldn't be any faster/slower in general between, for example, Javascript and Lisp. You give them to me and I'll do it much faster in Lisp, while a javscript developer will do it much faster in javascript just because the task is simple enough that individual proficiency with the tools trumps any minor differences for such tasks.
Because in a discussion with five CS people, only one has actual real Lisp programming experience, one has some freshman experience in some professor's one-weekend Lisp-like dialect where a run-time type error segfaults, and the other three have different misconceptions about what Lisp is.
I suspect that 1 in 5 might actually be high, which IMHO is sad -- not because I think less of those without any kind of Lisp experience, but because I think they are seriously missing out.
For any CS folks (or just developers in general) who have thought about learning a Lisp of some sort, but for some reason have never gotten it done, I would encourage you to do so -- it really is worth the effort.
To clear it up for anyone reading this:
Scheme has tail-call elimination as a requirement of the language; common lisp does not, and even includes an (overly?) sophisticated DSL for iteration in the standard library.
Also 95% of what you learned about lisp in your "Survey of programing languages" course was wrong.
[1] https://docs.oracle.com/javase/8/docs/api/java/util/stream/C...
I regret not knowing more about them as a younger programmer. Most of the "power" of lisp is always shown in the ability to compose programs out of forms. It is amazing how much code you can avoid writing by just using either loop or format. Or both.
On the other hand, Lisp languages (affectionately called "programmable programming languages"), remove that separation almost completely. As a programmer using a Lisp, you're provided with enough tools to build new syntax and semantic rules, so that the language better matches your problem domain. This is a very powerful technique, but obviously also dangerous.
As a simple example, say you wanted to do OOP in Python, but for some reason Python didn't provide the "class" keyword. At this point, you'd be totally out of luck. Your best bet would be to send a request to language designers, and in the meantime try to program OOP-style in structural code. On the other hand, if someone took away the "defclass" macro from Common Lisp, you could quickly reimplement it yourself and thus introduce OOP facilities that look and work as if they were a part of the language from the start (in fact, building your own simple OOP is pretty much a textbook exercise for lispers).
That much of power can obviously confuse people who didn't do much programming before. It's good to start with a simpler language first - be it something like Python, or even subset of Scheme that's used in SICP, which sacrifices some of the Lisp power for making it suitable as an introduction to programming.
Personally I learned the appeal of lisp while following this tutorial on writing a scheme in Haskell: https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_.... There's a reason there'll never be a tutorial like that for a toy Haskell interpreter.
A Scheme that someone wrote in 48 hours following a tutorial isn't something I'd want to use in production. It's missing the whole angle of Lisp being a feature-rich, mature language suitable for production use.
A Lisp is valuable because of what you don't have to write in it to get the job done.
The post you replied to, OTOH, was arguing that writing one's own Lisp is sufficient, and I think it's right: if one implements Lisp from scratch, one will end up appreciating Lisp. Outside of half-assed homework assignments, anyway.
To appreciate Lisp (or anything) is really to appreciate the underlying requirements (and that they are done).
Some people absorb requirements very well (and then appreciate them) if they force themselves to implement the requirements, which is fine. Even those people can't just confine their appreciation to that which they're able to implement, because then the scope of their appreciation will be rather small.
I really appreciate John Carmack's approach to determining shadow maps for a scene: the clever "cast a ray from a point on the surface to infinity and determine whether it intersects a shadow volume boundary an even or odd number of times" so then you know whether it's in the shadow volume (w.r.t a light source) or not. So we go backwards from a pixel on the screen, to some point on an object's surface. For each such point (consequently pixel) we can determine whether it's in shadow; and we can reduce that to a 2D shadow map in pixel space. Terrific, right? And about the shadow volumes, they are basically just conical shapes made using the 2D contour of the objects as seen from the POV of the light source, and capped at the end somewhere so they are finite. I'm not going to sit here and implement it all though. It's not that I couldn't find the time but that it's out of scope. I can play it out in my head though and see it working which is a good enough facsimile.
I was initially repulsed when I saw examples of Lisp code. After I had been in the static, Algol-like world for many years (Pascal, C, Ada, C++) I encountered more dynamic languages like Perl, Java, Python, and Ruby. I was fascinated by things you could do in those languages (especially Python and Ruby), and then learned that Lisp had been doing these kinds of things better for decades. That got me interested enough to move beyond the dislike of things like parentheses.
However, now things are different. There are now several choices of languages that have a mix of expressive power and efficiency that rival or surpass Lisp such that it doesn't stand out as much. I would say it's ability to be customized is now the main feature and curse of the language. It causes some people to love it, but makes it unsuitable for the typical large team project unless you have very strict controls on how it is used.
Common Lisp has the advantage of having done a pretty good job at the hard problem of naming things and so ordinary activities have names with regular semantics like |remove| and |remove-if| -- generally |some-name| tests against a value and |some-name-if| tests against a predicate. Functions on sequences have keywords as supporting parts of speech that make it possible to state intent rather than write an implementation -- Common Lisp contains several domain specific languages that have proven to be useful.
One way to learn them is to read Steele's, Common Lisp: the language.
It's not that a programmer can't write their own version of |copy-tree| and a naive implementation is fairly easy. It's not but nominally more difficult or verbose than writing it in a Scheme or Clojure. The difference is that Common Lisp's built in |copy-tree| is probably sophisticated for performance and robustness reasons. Schemes come out of a pedagogical context and there not having a built-in |copy-tree| can be a feature because of what people may learn by its absence. Racket's Student Languages push this toward the limit.
To put it another way, Assembly Code has fewer constructs than Python, but often there is an advantage that comes with Python's higher level abstractions.
[1] Why the hurry: http://norvig.com/21-days.html
There's more stuff in Common Lisp, but once I learned the concepts behind how the naming worked, a lot of them didn't need to be learned as much as I just assumed they were there.
The "standard library" is large, but really well thought out. Writing in Scheme, I'd often need to implement functions that I knew were already in CL, so Scheme ends up being more work in the long run.
And it's not like a person needs to memorize all of the language constructs before using it. The REPL in Slime is incredibly helpful in discovering functions and verifying how they work, and Stack Overflow or web search can be really helpful finding out if a particular function is built-in or not.
Racket has a minor language for shell mix-ins[1] and Racket itself has some nice ways of dealing with command-line arguments[2]. However, all command-line arguments are passed as strings, which means extra steps in converting them to native types (numbers etc.). I find it very pleasant and Racket in particular has both a good standard library and a whole bunch of libraries that probably cover a majority of what one might need for smaller tasks. But again, it depends on what you consider "small-scale programming"
[1]: http://docs.racket-lang.org/rash/index.html
[2]: http://docs.racket-lang.org/reference/Command-Line_Parsing.h...
Personally, I use Chicken Scheme for command-line utils (thanks to AOT compilation Chicken Scheme programs start-up time is much shorter) and Racket for GUI programs. Both implementations offer stability, ongoing development and a package ecosystem which has most of the things I need. They also provide extensive tooling, with Racket going as far as providing an IDE and visual debugger.
Most of the problems you'd have when replacing PERL or Ruby with a Scheme would have to do with library/package availability. If your use-case doesn't require a library which your Scheme lacks it may be a suitable replacement for PERL, Ruby, Python or JavaScript (node).
Racket is not just Scheme anymore, but it does still support various versions of Scheme as well as a number of Scheme-based languages. It has a package management system and a fairly large set of packages for basic tasks. The core system, base libraries, and major libraries are well documented and it's got a great built-in IDE.
Guile also does more than just Scheme, but it's been the "official extension language of GNU" for a very long time, so it's got bindings to a lot of libraries and it's used in some interesting applications and tools.
Chicken Scheme also comes with a package system, 'eggs', that has a pretty nice collection of practical packages available for easy installation. You can run your programs via the interpreter, or you can compile them via the compiler, which goes through C so it's got great C interop.
There are a lot of other options with a variety of strengths and weaknesses, but those are good ones to look at for starting out and doing scripting duties.
Then here are some very nice SRFIs around to do most of what you want, and I believe chicken comes with basic pattern matching facilities as well (although not as sweet as the ML-type which are checked at compile time).
Scheme and CL bicker over whether an empty list embodies the concept of falsehood. JavaScript says wat? And Haskell is like, lol.
Scheme of 1975 was minimalist and it lacked a lot of things you need, like error handling. Since then Scheme went through various rounds of standardisation - currently Scheme R7RS large is in the works. Plus there are many quasi-standard extensions, based on SRFI: https://srfi.schemers.org.
If you add the usual stuff to Scheme then it is as large as Common Lisp.
Also Scheme is not generally simpler than Common Lisp. Macro programming or control flow in Scheme is not simpler than Common Lisp.
What is simpler than Common Lisp, is the core language used in education to teach programming concepts. For example in SICP a simple Scheme subset is used without any macro programming. But any practical variant of Scheme will quickly approach and sometimes even surpass Common Lisp.
Scheme R6RS Standard:
http://www.r6rs.org -> Standard + standard libs + appendices
Guile Manual:
https://www.gnu.org/software/guile/manual/html_node/index.ht...
Chez Scheme User's guide:
http://cisco.github.io/ChezScheme/csug9.4/csug.html
Racket Core Language:
There are 25 special forms in Lisp, and the standard forbids the creation of more: http://www.lispworks.com/documentation/HyperSpec/Body/03_aba...
MIT Scheme has 30 special forms: https://www.gnu.org/software/mit-scheme/documentation/mit-sc...
R7RS doesn't seem to define special forms per se; it considers anything other than define-syntax, literals, variables, calls, lambda, if & set! to be derived expression types; I think quote & quasiquote probably have to be considered special forms in at least some senses.
Scheme itself is a minimalist language, but a practical Scheme must add in plenty of functionality not specified in the standard. A truly useful Scheme will thus be roughly as big as Common Lisp, or bigger — only much less of that bulk will necessarily be standardised & portable.
CL can handle a large SLOC, as evidenced by the fact that the huge compiler such as SBCL itself is written in CL. But it won't be as popular as Java or C++ are, so it would become a problem when CL faces a project which is "large as in company".
CLer: Your language isn't suited for the enterprise. You need a hundred developers to do what we do with five.
There are some CL code bases with millions of lines. But even a million line in todays terms is not much for some companies which are using other languages. If we look back, the operating system and basic environment (GUI, IDE, basic apps) of the Lisp Machine OS was around 500 kloc. Later versions with more stuff and some applications were nearing 2 Mloc. The commercial CL companies (Franz, Harlequin/LispWorks, Lucid, Xerox, TI, LMI, Apple, Goldhill, DEC, ...) usually should have developed code bases with +1MLOC already in the 80s / early 90s. Some Common Lisp based CAD software was/is in the range of 5-10 MLoc. Some application code might be much larger. For example Boeing has whole jet aircrafts designed in Lisp using the now discontinued iCAD application.
> so it would become a problem when CL faces a project which is "large as in company".
Common Lisp has been used in environments with up to 300 developers. This is not really a large number, but a larger number of what people typically think of a Lisp project. Symbolics, the Lisp Machine company, had at max 1000 employees - with probably a third of that software developers. Lucent developed network switches in the 80s/90s with 100+ Lisp developers. ITA was developing their flight search engine with a 100+ team with a lot Lisp developers. There are also smaller companies with a long history - for example Cyc is being developed using Lisp since thirty years.
One problem was/is finding these people. An old saying was that there were more Lisp Machines than developers able to use them. Today it would be even more of a problem, since the number of interesting Lisp projects at universities is smaller than what we had in the 80s/90s. Thus fewer students get in contact.
Probably a good idea to visit the European Lisp Symposium in Brussels, April 2017. Meet some fellow Lisp users from academia, industry, ...
CL-USER> (ql:quickload :trivia)
... loads ...
CL-USER> (asdf:test-system :trivia)
... test output ...
But unfortunately not all libraries set up their test-ops properly. You can usually find a readme at ~/quicklisp/dists/quicklisp/software/... which might have instructions for running tests.[1] https://github.com/cl-test-grid/cl-test-grid [2] https://common-lisp.net/project/cl-test-grid/library/
Clack, whilst being a great concept suffers from a lack of documentation. What documentation is there is confusing. It seems Clack has in the past couple of years been split out into a separate project - Lack. There's no documentation around as to why this has been done. I can't find details on how to write middleware for it. I need to look through the source.
I'm trying to get Ningle working. The Redis session middleware seems broken. It doesn't seem thread safe at all. I seem to have other problems with by body parameters being mutated as it passes through the middleware.
I have gotten very close to just dropping Clack and moving onto raw Huchentoot or Allegro serve, but it saddens me that a project with such promise shouldn't get the love it deserves so I will probably persist and hopefully help to improve the project.
Don't get me wrong, I love Common Lisp as a language and use it daily. But problems like this are just standard.
As far as web projects goes, this looks promising: https://github.com/Shirakumo/radiance Shinmera puts a lot of effort into finishing his libraries and documenting them well, so I expect that this library would be nicer to use than many of the alternatives.
But to everyone who wants to get started with Lisp, I can only recommend not to focus on libraries first. Between what the language includes (especially Common Lisp), and what the implementation ships with, you should be set up to do a lot of serious programming. If there is anything, which you cannot do with the default environment and with what you cannot implement faster yourself, then its time to use quicklisp to add the missing pieces. But having written some pretty large Common Lisp programs myself, they tend to depend on only a very few external libraries.
A lot of people (likely mistakenly) think that it's just a language you learn in CS classes and then leave behind.
Example: https://github.com/acl2/acl2/tree/master/books/rtl/rel11
> This directory contains an ACL2 library of register-transfer logic, developed at AMD from 1995 to 2011, and at Intel from 2012 to the present, in support of the mechanical verification of the floating-point arithmetic units designed during those periods by the respective companies.
$ time sbcl --script hello.lisp
Hello World!
real 0m0.026s
user 0m0.009s
sys 0m0.013s
Compared with languages with a much bigger starting curve: $ time node hello.js
Hello World!
real 0m0.113s
user 0m0.052s
sys 0m0.022s"Imperative programming" might be another one. IRL, think about how "cisgender" has entered into mainstream vocabulary in the past few years, or "heterosexual" many years before that.
So ahead-of-time compilation, as linguistic term and CS literature, was born as a way to differentiate from dynamic compilation.
(I was a grad student in one of his classes many years ago.)
The Clojure Inquisition stops by to remind everybody that what really matters in programming languages is how many hipsters you can attract into your fan base.
I emphasize this because you posted https://news.ycombinator.com/item?id=13943374 a few days ago, which was a particularly dismaying example. Comments like that basically activate a bug in human firmware to the detriment of substantive discussion.
If you think I'm being overbroad in my generalizations, of course I am. As they say, "That's the joke."
I'll stop but only under protest, and with the note that I think HN administration is misreading the effect this has on people. (Hint: it's funny because the average Hackernews sees a little of themself in the n-gate posts, not because they feel smugly superior.)