Marvin Minsky – The beauty of the Lisp language
webofstories.comPlease resubmit with the actual story, not your G+ profile link:
I got great grades in college, but could never wrap my head around LISP (Lost In Stupid Parentheses). For anything non-trivial, I cheated and handed in somebody else's work.
There, I said it.
I've been writing a book on Clojure, "Clojure for the Brave and True": http://www.braveclojure.com. It's meant to be an entertaining guide which focuses on getting you up and running quickly. It also explains the more novel parts of lisp in a clear, non-academic way.
It's free online and is going to be published by No Starch eventually. Hope you find it useful!
Your book is beautiful! Highly recommend! I very much like your style!
(there are some formatting errors though, like unresolved org-mode symbols)
Thanks :D
I've been trying to find and fix the formatting errors. I think most of them are fixed by now. At least, I hope so!
Strange. I never learned it in college, just on my own for fun, but I've found it to be quite elegant (if impractical compared to better modern languages). The fact that everything is an expression and you can use any expression anywhere makes it very easy to learn, and powerful.
I personally find it more enjoyable to use than things like C. Then again, for one historical reason or another, C-type languages won, and nowadays are the easiest path to making software, since you'll inevitably have to interact with an OS, window toolkit, libraries, etc..., that all have a C/C++ interface...
>> impractical compared to better modern languages
How much did 'they' pay you to say that? :D
But seriously Lisps are by far the most advanced programming languages. They just take a huge amount of effort to learn.
> How much did 'they' pay you to say that? :D
I wish.
Seriously though, most modern languages have Lisp features. Even Java 8 will get Lambdas. Quite a few languages have macros. Many languages have eval and REPLs.
Right now I'm playing around quite a bit with Haxe. Cool language, static typing (w/ type inference), pattern matching, closures, macros, and compiles to half a dozen languages and nearly every platform known to mankind...
Unfortunately I think many people get "Lost In Stupid Parenthesis" because they either refuse to use or are not supplied with the proper tools. Lisp programs are of such an elegant structure form that they lend them selves to highly structured editing tools.
I use CL in my day job and would find it highly painful if I did not have all the editing tools I use. Basic things like auto closing parenthesis; this makes the whole I need to balance my parenthesis issue disappear, coupled with transforming existing symbolic expression.
> I use CL in my day job and would find it highly painful if I did not have all the editing tools I use.
Just curious, what kind of things do you do with CL at work? I'm always interested in how people use less-common languages :)
I love hearing about that sort of stuff too... Especially video games (GOOL and http://en.wikipedia.org/wiki/Game_Oriented_Assembly_Lisp and https://play.google.com/store/apps/details?id=com.friendlyvi...) and Mars rovers (http://www.flownet.com/gat/jpl-lisp.html) and other things that now are dominated by other languages. With Lisp, I'm always reminded of this quote:
"Please don't assume Lisp is only useful for Animation and Graphics, AI, Bioinformatics, B2B and E-Commerce, Data Mining, EDA/Semiconductor applications, Expert Systems, Finance, Intelligent Agents, Knowledge Management, Mechanical CAD, Modeling and Simulation, Natural Language, Optimization, Research, Risk Analysis, Scheduling, Telecom, and Web Authoring just because these are the only things they happened to list." --Kent Pitman
We do digital optimization
The modelling is done in MatLab but the automated experimentation platform is all CL.
I felt the same way about lisp and always reached for the same acronym when I was in college at UT. 7 years later I'm a huge fan of lisp (CL/Clojure/Scheme) and enjoy writing Clojure whenever I get the chance.
It's hard to appreciate the elegance of the language before experiencing years of pain working with others. I have similar feelings about learning Haskell (and not appreciating it) in college.
I highly recommend buying a copy of Structure and Interpretation of Computer Programs (SICP), downloading Racket http://racket-lang.org, and learning you some Scheme (err, Racket).
I took a class centered around SICP my first term in college, and it was an incredibly enlightening experience.
While SICP is a wonderful book and should be recommended, it is not meant as a tutorial on Scheme. SICP only uses Scheme to have some concrete language to talk about deeper themes. The grandparent might have better luck with a book that is about Scheme.
By contrast, I recall the story of a guy who had the choice between C and Lisp for his programming class, and chose Lisp because he couldn't figure out C.
IIRC Richard Stallman also has a story about secretaries at MIT using Lisp for basically the same reason.
Actually Stallman placed preference on Lisp and C as being his preferred languages.
The quote your referring to is part of his statement exposing the imbalance of the the educational system within the US in the 80's. Here is the quote which also came from the first announcement to his emacs editor under the tag /Blue Sky/:
When large numbers of nontechnical workers are using a programmable editor, they will he tempted constantly to begin programming in the course of their day-to-day lives. This should contribute greatly to computer literacy, especially because many of the people thus exposed will be secretaries taught by society that they are incapable of doing mathematics, and unable to imagine for a moment that they can learn to program. But that won't stop them from learning it if they don't know that it is programming that they are learning!
My prob wasn't really about being lost in parenthesis; I had an editor with bracket matching. My prob was the "inside-out" thinking that LISP requires. It's tricky when you're coming from a BASIC/PASCAL/C background.
I'm a sucker for punishment, so after my first (bad) taste of LISP, I took an AI course that was 100% LISP. My AI teacher studied under McCarthy at Stanford, so I probably can't blame him for anything.
In clojure you have thread macros to solve the “inside-out” problem.
(-> 3 (+ 6) (* 7) (/ 12))
or (just an example, fictional names)
(->> object reflector fields (map :balance) (filter #(> % 1000))
Which reads like (pseudo language)
object.reflector().fields().map(:balance).filter(field -> field > 1000)
Did you try thinking aloud what you were writing? Like, while writing (+ 5 6), saying "Sum of five and six." I found the prefix style a little odd until I started doing that.
I get the impression Lisp tended to be taught awfully, uninspiringly. Maybe even worse than other programming languages. Cheating sounds like a reasonable response to such a farce of "education"; sad to hear when exposure to Lisp is a soul-crushing experience.
I've seen a few old AI books where Lisp code is formatted unreadably. I wonder why.
Georgia Tech, one of the top schools in the US, couldn't even teach lisp. They tried scheme with SICP (IIRC, not sure what else would have been used in 2001). It ended with a major cheating scandal [1]. I was convinced at the time the issue was a lack of understanding on the parts of the TAs. Very few of them had a background in scheme, it was the blind leading the blind according to friends who had started that year (2001, I started in 2000). Since so many students took intro to computing and there were only a few lecturers, the TAs had to do the bulk of the real instruction (that is, non-200+ student lectures). This resulted (at least for a time) in a fragmenting of the introductory programming material, and it leaving the hands of the College of Computing. Non-CS/EE/CMPE engineering students ended up in courses using matlab, without ever actually studying algorithm design/analysis (which is what CS 1311/1321/1511/whatever other numbers they'd used previously) used to cover.
[1] http://www.cavalierdaily.com/article/2002/01/cheating-scanda...
At least as far as I can tell, the problem was that they required everyone to learn scheme, including the non-CS majors.
I don't think that was the problem, but it was a problem. I still contend the inability of the instructors to do their job was the problem. If the TAs had been prepared to teach scheme, and not come out of a year with a background in pseudocode (based on Pascal for CS 1311/1321/1511/too many other numbers) and Java (CS 1312/1322/1512/too many more numbers), but instead had a background in functional programming and scheme in particular, they might have been able to help their students out. That transition period from the pseudocode based intro to computing to the scheme/SICP based course should have been more gradual. Which leaves the ultimate failure on the part of the faculty who decided on an abrupt transition for their students, and ultimately failed the students in the process.
The Little Schemer is think is one of the most inspiring ways to learn. Truly a book every software engineer should enjoy.
The sequel books are also work looking at (The Seasoned Schemer, The Reasoned Schemer).
What does he mean when he says that it's not possible to write a C program that can write other C programs?
Seems like there are counterexamples here: http://www.nyx.net/~gthompso/quine.htm
Furthermore, it's possible to encode another programs' code into a single print statement, thereby writing one piece of code with another. As that's trivial, I suspect he meant something else. What was it? :).
Quines are not quite the same thing.
Reasonably simple illustration: in Common Lisp, it is possible to write a function that takes a mathematical expression as an argument and returns another expression, as its derivative.
So basically I can write (pseudo-lisp-ish):
(defun diff (f) ... )
then call it like this:
(diff '(+ (pow x 2) x)
and I'd get the expression
(+ (* 2 x) 1)
as a result. The point is, however, that this expression is Lisp-callable code. If I already have a function p, I can write:
(setq dp (diff p))
and dp is now the function computed by (diff p).
This is not necessarily the most practically-useful example, but I think it's closest to being a strong illustration of the principle. In a nutshell, you can do symbolic manipulation with code from within your code.
Edit: I just read your other post from this thread. It's important to note that this happens at runtime, not at compile time.
This isn't impossible to do in C if you really want, but it wouldn't be portable (the reason for this is left at the user's discretion). Think about how you would do the same in C: write a function that takes a pointer to a function f, and returns a pointer to another function g, so that g is the derivative of f. It's obviously ok to restrict f to common mathematical operators.
I can think about ways of doing it, but it ain't pretty.
Actually if you had to do this kind of thing in C or any other non-homoiconic language, the right way to go would be to define data structures that represent your mathematical functions, and manipulate these structures to compute the derivatives (by pattern matching of the represented mathematical expression, exactly as you would do in Lisp using Lisp code which kind of is its own AST). And you would have to embed an interpreter for the mathematical expressions encoded in the data structures of your program, so you can not only manipulate the expressions, but also use them (which also comes for free in Lisp).
See Greenspun's tenth rule of programming.
> Actually if you had to do this kind of thing in C or any other non-homoiconic language, the right way to go would be to define data structures that represent your mathematical functions, and manipulate these structures to compute the derivatives (by pattern matching of the represented mathematical expression, exactly as you would do in Lisp using Lisp code which kind of is its own AST).
Yes, of course. The language, being non-homoiconic, would require you to translate between a data representation that you can process and the data representation that the runtime can process.
However, if you were to do the equivalent thing -- that is, obtain the derivative based on the data representation that your runtime can process -- you'd have to examine the contents of the program memory itself. Needless to say, that would make things even unportable, or outright impossible due to compiler optimizations. But it would be the same thing :).
Edit: perversity bonus, you're working on a computer that does bank switching and the code that is computing the derivative is in a different memory bank than the function whose derivative it must compute.
And then there is this whole other category of self-modifying programs - programs that know about their own structure and can change themselves at runtime. This is (almost) in the "unthinkable" category for non-homoiconic languages.
This was, in fact, more or less pioneered in languages that aren't homoiconic in the same manner as Lisp is (code that used self-modification for reasons like time or space optimization was, if not common, at least known of in the early days of electronic computers). Arguably, machine code is very much homoiconic; what is beautiful about Lisp is that the underlying structure of program/data is regular and nicely-abstracted.
I think you could solve most of the problems that you'd need this property for with closures. If the bulk of the execution is defined with them, and you have closures defining which other closures to run, you can have them swap each other out.
I'm not sure I understand what you have in mind. Assuming I have something like this:
int foo(void) { return bar(1) + 2; }
is there any way in which I could arbitrarily modify this function? E.g. to dynamically turn it into
int foo(void) { return bar(2) * 4 }
using closures?
I get how you could do that, assuming you had a function defined for each of the possible operations you needed. But what about arbitrary modifications?
You wouldn't want to modify the function in-place, that would violate immutability of data. Each of these closures would themselves be generated, by a factory method. When you're ready to modify, you'd invoke the factory to generate another closure, then you'd use whatever infrastructure you built to assign them in the first place to re-assign them to the new closures.
Ah, got it. Thanks!
If you wanted to get really sophisticated, you could generate the factory methods, too.
I suspect "possible" here is not used in the absolute sense, but more just "it would be a total pain in the ass in C, whereas Lisp excels at it".
After all, anything is "possible" with hand-written assembly code, too, but realistically speaking, it's not practical.
I couldn't post this last night, but yeah:
My ears perked up at the dual statement that although you can write a Lisp program that writes Lisp programs, nobody does it (um, macros?); and that you can't write a C program that writes a C programs (okay, maybe nobody does this, but you certainly can). I guess he means that C is not "aware" of its own constructs the way that Lisp is.
Also, he said that the algebraic languages were "dying out", but that because of this self-generating ability (which nobody uses), that the future of Lisp was "open".
Huh?
I understood the "open" comment to mean "open to modification and extension", independent of whether it is dying out.
I think you might have missed the point of the statement "programs that write programs", it goes far beyond a toy quine.
You can't do it in C unless you also implement a compiler or evaluator. And even then generating any sort of reasonably complex C code with C would be highly painful to say the least.
You can possibly nearly get there with C macros but I would argue it's not C writing C there, rather a text preprocessor.
> You can't do it in C unless you also implement a compiler or evaluator.
Someone else already did that work. Your C compiler is very likely, in fact, already written in C. We don't expect a C-importable library interface for the C compiler, because none of the traditional old C compilers had one (gcc, MSVC, etc.) But libclang exists, and it's pretty easy to write a C REPL using it. From there, only a few steps to automation.
He's most likely referring to the homoiconicity of lisp (http://en.wikipedia.org/wiki/Homoiconicity) - when processor operations are a data type, writing a subprogram and executing it is as simple as filling in an array. In C, you would need to have a C compiler and a virtual machine in your program to run it!
He's talking about macro in Lisp and C. see: http://en.wikipedia.org/wiki/Macro_(computer_science) The macro in Lisp provides you with much more convenience (and power?) than print statement.
Lisp macroes have access to code that has already been compiled (which means, everything above the macro, and everything defined in imported files). This gives you access to the entire standard library in your macro.
Also, because Lisp code is data rather than strings (code is represented as linked lists) it's much simpler to manipulate code in Lisp macros.
First by 'cannot' he means 'cannot easily', as other stated it's more than printing source code, and goes into metaprogramming quickly, and high cost resistance will render attempts at doing so in C-like languages painfull.
Also, historical context matters, in the late 50s I believe this was very foreign to how machines were seen and used.
The Minsky clip was great, but I just spent an hour watching parts of the interview with Knuth. 50 years writing a book - there's a book about the book in there waiting.