"Lisp is a medium for working with a computation until it is in balance with its external and internal requirements. At that point it can be decorated with performance-enhancing declarations and perhaps additionally modularized. In this it is more like an artist's medium than what many think of as a programming language."
"Writing a beautiful text takes sitting before a medium that one can revise easily, and allowing a combination of flow and revision to take place as the outlines and then the details of the piece come into view. Changes are made as features of the writing emerge—first the noise, then the sense, the force, and then the meaning—and the work of the writer is to continually improve the work until it represents the best words in the best order. Great writing is never accomplished through planning followed by implementation in words, because the nature of the word choices, phrasings, sentence structures, paragraph structures, and narrative structures interact in ways that depend of each other, and the only possible plan that can be made with which a writer can "reason" about the piece is the piece itself."
"The difference between Lisp and Java, as Paul Graham has pointed out, is that Lisp is for working with computational ideas and expression, whereas Java is for expressing completed programs. As James [Gosling] says, Java requires you to pin down decisions early on. And once pinned down, the system which is the set of type declarations, the compiler, and the runtime system make it as hard as it can for you to change those assumptions, on the assumption that all such changes are mistakes you're inadvertently making. There are, of course, many situations when making change more difficult is the best thing to do: Once a program is perfected, for example, or when it is put into light-maintenance mode. But when we are exploring what to create given a trigger or other impetus—when we are in flow—we need to change things frequently, even while we want the system to be robust in the face of such changes."
>"The difference between Lisp and Java, as Paul Graham has pointed out, is that Lisp is for working with computational ideas and expression, whereas Java is for expressing completed programs. As James [Gosling] says, Java requires you to pin down decisions early on..."
My experience with modifying Lisp and Java programs has largely been the opposite. I've been working on a Java application for the past few weeks, and the design of the program has changed several times as I've thought of new features to add and cleaner ways to organize my code. Today, I had a class X that was responsible for keeping track of a certain variable, and I wanted to move that responsibility to class Y. I deleted the relevant field and all references to it in class X, and Eclipse highlighted all of the errors that change introduced. I fixed the highlighted errors and added the new methods I needed to class Y, and when I tested the relevant functionality, I saw that I had introduced only one minor bug, which I fixed with a single line of code. There was nothing difficult about the process.
I've found refactoring Lisp programs to be more difficult. When I was writing a prototype of this same application in Lisp (Clojure more specifically), I had a macro to implement a small DSL. The macro was not particularly complex, although it was non-trivial: it did some code walking and called some helper functions. When my DSL grew past a certain size, making changes to its implementation became rather difficult. Sometimes the compiler would report errors at runtime, but more often than not, things would just stop working in mysterious ways whenever I made a change. The macro's output was also quite different than the kind of code I would write by hand, so debugging was difficult.
I'm not the best Lisp programmer, so I'm not trying to say that Lisp always makes it hard to change your programs. But there are cases where Java makes it very easy to change your programs.
Tooling around Java (e.g. Eclipse) has developed significantly since the time this article was written. If you're working in programs where the system is too big to fit in your mind, good tooling certainly helps. Lisp is especially powerful for programmers who are able to work with large and complex mental models of systems.
and
Debugging Lisp in Lisp is far superior (easier) compared to debugging Lisp in Java (which is what you get when you use Clojure). Clojure has been getting a lot of press as being the rebirth of Lisp, and it includes some useful paradigms in the core language, but it is woefully incomplete as a full Lisp development system when compared to mature Common Lisp implementations.
Out of curiosity, what do you see as the difference between Common Lisp debugging and Clojure debugging? I'm mostly experienced with Clojure (my CL code consists of a few trivial programs), but it seems like Lisp in the right environment (Emacs, paredit, SLIME/nrepl) is a joy regardless of which Lisp.
As someone who writes academically and writes code in the form of research prototypes, I really have to disagree to some extent with RPG and PG. Code is code, it doesn't have to be elegant, it doesn't have to be read, and using a less elegant language (like say C#) can be liberating compared to one where more elegance is possible (say Scala). The architecture you are working on floats around in your head regardless, becoming more beautiful, and the language that it is expressed in is just a detail...it is easy enough to throw the code away and start again once it becomes too unwieldy and you need to take your design to the next level.
Common Lisp was designed for large, complex applications where it is not "easy enough" to throw code away but existing codebases must be evolved in-place. It is a practical language, not an academic language.
In academia, your code is a reflection of your conception and can be changed when the conception changes.
In the "real world" the satellite you already launched into orbit needs a new feature.
Nobody is writing satellite code in CLOS, though. I like CLOS, I still get many new ideas from that language (I'm in PL), but claiming it is a language designed for scalability is a bit weird. Even C++ would be a bit more safer depending on what flavor of CLOS you are using.
My point is that C# is a good enough language for rapid prototyping; that the premature commitment problem is not a big deal in practice. I couldn't really imagine the argument evolving into "premature commitment AND rewriting code is bad" in any coherent manner.
You're looking at the "Projects" list of other projects on the site. Spike itself is written in Common Lisp. You can read some of the papers on the "technical details" of Spike page for confirmation: http://www.stsci.edu/institute/software_hardware/spike/detai...
Even C++ would be a bit more safer depending on what flavor of CLOS you are using.
Assuming you meant CL rather than CLOS, I disagree with intensity. Considering CL is actually memory safe, and has the condition system, this this is the weird statement.
CL was used for building a lot of big and expensive software, and still is in certain niches. I say actual reality trumps HN PL theorizing. True, there are challenges, but somehow actual humans have managed to get a lot of really good work done with this language.
> At the time it was more or less taken for granted that AI work was done in Lisp. C++ barely existed. Perl was brand new. Java was years away. Spacecraft were mostly programmed in assembler, or, if you were really being radical, Ada.
Then of course there was resistance:
> Now, you might expect that with a track record like that, with one technological success after another, that NASA would be rushing to embrace Lisp. And you would, of course, be wrong. ... Someone (I don't know who) interrupted him and asked if he could change only one thing to make things better what would it be. His answer was: get rid of Lisp [3].
And then...
> The demise of Lisp at JPL is a tragedy. The language is particularly well suited for the kind of software development that is often done here: one-of-a-kind, highly dynamic applications that must be developed on extremely tight budgets and schedules. The efficacy of the language in that kind of environment is amply documented by a long record of unmatched technical achievements.
Lispers always wonder what could have been, what should be, but then there is what is.
Don't get me wrong, I like Lisp, I've used it, I've never used CLOS for anything big but it is definitely a relevant language in the study of PL. But the sad truth is that...LISP is kind of weird unapproachable to most people: if they want semantic feedback (type checking), then there are other choices (Java, C++), if they want a dynamic language with a more well defined semantics and syntax, there are better options (Python).
Sure. In my defense, the real world doesn't require great poetic academic writing skills either. Specs and email are quite different. Life is quite different when you are writing code to have something to write about.
This person isn't a very good writer. I've read through it once and I'm not sure what the point is supposed to be. LISP and Java are different -- ok, that's pretty obvious. Some fuzzy stuff about embracing creativity, ok, fair enough but also fairly trite and more suited to an Apple TV commercial. Programming is an art as well as a science, yes, that's nice. Some things are plainly false, for example, "the theory of quantum mechanics and the expanding plausibility of complexity science threw everything we knew about the world into doubt." Did it really? So before we knew that London was the capital of the UK and that 2+2=4 but the "expanding plausibility of complexity science" caused people to doubt that?
"Lisp is the language of loveliness. With it a great programmer can make a beautiful, operating thing, a thing organically created and formed through the interaction of a programmer/artist and a medium of expression that happens to execute on a computer."
It's this kind of statement that makes me suspect Lisp is not very practical. I'm not programming for the sake of programming to create something that "happens to execute on a computer." There are other artistic endeavors for my free time that are more interesting than a lovely computer language.
So in your estimation all other computer languages are walkers compared to the beautiful dance of Lisp? I'm curious, maybe I really do need to learn Lisp.
That is my estimation. If you don't need performance or macros, Python and Ruby may give you a similar development experience. However, neither of those languages AFAIK allows you to redefine (let alone recompile to native code) functions at runtime.
Ruby is an extremely dynamic language, where you can redefine almost anything at runtime:
def f()
puts "f1"
end
f()
if true
def f()
puts "true"
end
else
def f()
puts "false"
end
end
f()
This outputs:
f1
true
As for native code, apparently MacRuby [1] compiles to native code, although I've never used it, so I don't know the details of that claim.
Can you give an example where redefining a function at runtime is a useful feature to have? I don't mean this as a challenge, I'm just curious. I like the extreme flexibility that dynamic languages provide, but it seems like the most dynamic features are mainly useful for library writers.
I don't think this is what enduser means. Lisp is the only language i know of where you can run a function such as
(defun foo ()
(progn (format t "~a~%" (read-line))
(foo)))
and then, while it is recursing, change the function to
(defun foo ()
(progn (format t "~a and john~%" (read-line))
(foo)))
recompile it, and have the behavior of your program change, while it is running. This is part of what makes lisp excellent for rapid prototyping. I'd give more examples, but I'm lazy, and it's been properly described elsewhere.
$ irb
>> def go
> puts "what"
> debugger
> go
> end
>> go
"what"
# stops on debugger line
>> Object.module_eval "def go; puts 'wat'; end"
>> continue
"wat"
There are of course other ways to do that, I just picked a simple one. You can break at lines / statements in files, so you don't need to include that "debugger" line. You can do something other than Object.module_eval. You can use something like Pry that would make this simpler and give you in-line source code, even after editing (IIRC). You can use something like Hijack to modify any running Ruby process - when you disconnect, your modified code will still be running.
Lisp is not special in what it is capable of. Period. It's special because a) it's very old, and b) it's tuned for these features, in its own unique, minimalistic way.
Yes, not all languages can do this (though you can do crazy things with GDB / debuggers in general, including using it as a REPL for compiled languages (really! it's not hard!)). But Lisp / the Lisp family are not even remotely the only ones which can.
"What Made Lisp Different" - all true. All intended as past tense. I never see claims that Lisp is unique because it has GC, for instance.
Looking at the 'setf' macro, since you have to use 'defsetf', I don't see the difference between that and something in Ruby where you could do (errors aside):
class Object
def defsetf(name)
$setf[self.class] = name
end
end
def setf(class, *args)
class.send($setf[class], *args)
end
class Whatever
defsetf :set_something
def set_something; end
end
# elsewhere...
setf Whatever, instance, value
Adding arbitrarily-complex ways to let you specify how to transform the arguments to a call is as easy as accepting a lambda instead of a symbol, recursive setf-ing[1] is just a lookup on the arguments and then nesting lambdas. I might be missing some deeper realization here though - defsetf seems pretty simple. Certainly the use could be complex, but I see nothing that implies that its implementation is unachievable, even with nearly-identical syntax, so what remains is library support and community. Again - specialization, not unique attribute of the language.
As to "data-as-code", I think you mean the other way around? If not, eval. If yes, Ruby's reflection is I think insufficient without heavy modification like e.g. running a ruby interpreter in ruby to read its own source code and "reflect" with the AST that generates (though this is possible[2]). This is one spot where Lisp-likes certainly excel to a degree few outsiders achieve, but I'm sure I've seen outsiders, they're just not as fluent or efficient at running it. But few outsiders have real interest in optimizing it, or people label them as yet another Lisp-like, making the point sort of moot since it's part of the definition of being Lisp-like (to many).
--
Macros in particular come up often, but I don't see anything really interesting about them. If you have the full AST available (a rarity, I'll admit), then it seems like a strict subset of runtime modification - it's just a self-eliminating runtime modification, done ahead of time for speed purposes. Except for maybe C-style macros, which (via a horrible syntax) can generate anything, not just valid code, which isn't always possible.
What do you mean? Remember, that lisp isn't inherently a pure functional programming language. On the contrary, it's a very versatile multi-paradigm language.
I thought LISP was a functional language and I'm wondering how redefining a function at runtime is not a state change, but no matter, I'm reading Steve Yegge at the moment and his old take down of LISP which confirms my suspicions (don't you love it when you read something that does that ;-)
Thanks, that was a very interesting blog post. I feel like the comments add a lot of balance to the discussion (especially Pascal's, which Steve also mention in the blog itself).
There's no doubt that lisp has its problems, because if not, it would be a lot more popular today than is the case. Do note however, that a lot has changed since Steve posted that blog entry, most notably is the rise of clojure, which offers solutions to a lot of the problems mentioned.
Rich Hickey is a very interesting language designer and maybe the "benevolent dictator" that Lisp needs. Clojure works on the JVM and .Net as well as emitting JavaScript, so I'll probably take the plunge with it. I also thought the Light Table demo was extremely interesting. Good stuff.
36 comments
[ 5.2 ms ] story [ 92.1 ms ] thread"Lisp is a medium for working with a computation until it is in balance with its external and internal requirements. At that point it can be decorated with performance-enhancing declarations and perhaps additionally modularized. In this it is more like an artist's medium than what many think of as a programming language."
"Writing a beautiful text takes sitting before a medium that one can revise easily, and allowing a combination of flow and revision to take place as the outlines and then the details of the piece come into view. Changes are made as features of the writing emerge—first the noise, then the sense, the force, and then the meaning—and the work of the writer is to continually improve the work until it represents the best words in the best order. Great writing is never accomplished through planning followed by implementation in words, because the nature of the word choices, phrasings, sentence structures, paragraph structures, and narrative structures interact in ways that depend of each other, and the only possible plan that can be made with which a writer can "reason" about the piece is the piece itself."
"The difference between Lisp and Java, as Paul Graham has pointed out, is that Lisp is for working with computational ideas and expression, whereas Java is for expressing completed programs. As James [Gosling] says, Java requires you to pin down decisions early on. And once pinned down, the system which is the set of type declarations, the compiler, and the runtime system make it as hard as it can for you to change those assumptions, on the assumption that all such changes are mistakes you're inadvertently making. There are, of course, many situations when making change more difficult is the best thing to do: Once a program is perfected, for example, or when it is put into light-maintenance mode. But when we are exploring what to create given a trigger or other impetus—when we are in flow—we need to change things frequently, even while we want the system to be robust in the face of such changes."
My experience with modifying Lisp and Java programs has largely been the opposite. I've been working on a Java application for the past few weeks, and the design of the program has changed several times as I've thought of new features to add and cleaner ways to organize my code. Today, I had a class X that was responsible for keeping track of a certain variable, and I wanted to move that responsibility to class Y. I deleted the relevant field and all references to it in class X, and Eclipse highlighted all of the errors that change introduced. I fixed the highlighted errors and added the new methods I needed to class Y, and when I tested the relevant functionality, I saw that I had introduced only one minor bug, which I fixed with a single line of code. There was nothing difficult about the process.
I've found refactoring Lisp programs to be more difficult. When I was writing a prototype of this same application in Lisp (Clojure more specifically), I had a macro to implement a small DSL. The macro was not particularly complex, although it was non-trivial: it did some code walking and called some helper functions. When my DSL grew past a certain size, making changes to its implementation became rather difficult. Sometimes the compiler would report errors at runtime, but more often than not, things would just stop working in mysterious ways whenever I made a change. The macro's output was also quite different than the kind of code I would write by hand, so debugging was difficult.
I'm not the best Lisp programmer, so I'm not trying to say that Lisp always makes it hard to change your programs. But there are cases where Java makes it very easy to change your programs.
and
Debugging Lisp in Lisp is far superior (easier) compared to debugging Lisp in Java (which is what you get when you use Clojure). Clojure has been getting a lot of press as being the rebirth of Lisp, and it includes some useful paradigms in the core language, but it is woefully incomplete as a full Lisp development system when compared to mature Common Lisp implementations.
But then RPG also wrote "worse is better."
In academia, your code is a reflection of your conception and can be changed when the conception changes.
In the "real world" the satellite you already launched into orbit needs a new feature.
My point is that C# is a good enough language for rapid prototyping; that the premature commitment problem is not a big deal in practice. I couldn't really imagine the argument evolving into "premature commitment AND rewriting code is bad" in any coherent manner.
* satellite code written in Lisp
Also
Assuming you meant CL rather than CLOS, I disagree with intensity. Considering CL is actually memory safe, and has the condition system, this this is the weird statement.CL was used for building a lot of big and expensive software, and still is in certain niches. I say actual reality trumps HN PL theorizing. True, there are challenges, but somehow actual humans have managed to get a lot of really good work done with this language.
> At the time it was more or less taken for granted that AI work was done in Lisp. C++ barely existed. Perl was brand new. Java was years away. Spacecraft were mostly programmed in assembler, or, if you were really being radical, Ada.
Then of course there was resistance:
> Now, you might expect that with a track record like that, with one technological success after another, that NASA would be rushing to embrace Lisp. And you would, of course, be wrong. ... Someone (I don't know who) interrupted him and asked if he could change only one thing to make things better what would it be. His answer was: get rid of Lisp [3].
And then...
> The demise of Lisp at JPL is a tragedy. The language is particularly well suited for the kind of software development that is often done here: one-of-a-kind, highly dynamic applications that must be developed on extremely tight budgets and schedules. The efficacy of the language in that kind of environment is amply documented by a long record of unmatched technical achievements.
Lispers always wonder what could have been, what should be, but then there is what is.
Don't get me wrong, I like Lisp, I've used it, I've never used CLOS for anything big but it is definitely a relevant language in the study of PL. But the sad truth is that...LISP is kind of weird unapproachable to most people: if they want semantic feedback (type checking), then there are other choices (Java, C++), if they want a dynamic language with a more well defined semantics and syntax, there are better options (Python).
That pretty much sums up the difference between academia and the so called "real world".
In sum, a tedious and pretentious article.
It's this kind of statement that makes me suspect Lisp is not very practical. I'm not programming for the sake of programming to create something that "happens to execute on a computer." There are other artistic endeavors for my free time that are more interesting than a lovely computer language.
Can you give an example where redefining a function at runtime is a useful feature to have? I don't mean this as a challenge, I'm just curious. I like the extreme flexibility that dynamic languages provide, but it seems like the most dynamic features are mainly useful for library writers.
[1] http://stackoverflow.com/questions/1972388/is-it-possible-to...
Lisp is not special in what it is capable of. Period. It's special because a) it's very old, and b) it's tuned for these features, in its own unique, minimalistic way.
Yes, not all languages can do this (though you can do crazy things with GDB / debuggers in general, including using it as a REPL for compiled languages (really! it's not hard!)). But Lisp / the Lisp family are not even remotely the only ones which can.
With regards to the rest of your post: macros and data-as-code.
Have you read this http://www.paulgraham.com/diff.html ?
Looking at the 'setf' macro, since you have to use 'defsetf', I don't see the difference between that and something in Ruby where you could do (errors aside):
Adding arbitrarily-complex ways to let you specify how to transform the arguments to a call is as easy as accepting a lambda instead of a symbol, recursive setf-ing[1] is just a lookup on the arguments and then nesting lambdas. I might be missing some deeper realization here though - defsetf seems pretty simple. Certainly the use could be complex, but I see nothing that implies that its implementation is unachievable, even with nearly-identical syntax, so what remains is library support and community. Again - specialization, not unique attribute of the language.As to "data-as-code", I think you mean the other way around? If not, eval. If yes, Ruby's reflection is I think insufficient without heavy modification like e.g. running a ruby interpreter in ruby to read its own source code and "reflect" with the AST that generates (though this is possible[2]). This is one spot where Lisp-likes certainly excel to a degree few outsiders achieve, but I'm sure I've seen outsiders, they're just not as fluent or efficient at running it. But few outsiders have real interest in optimizing it, or people label them as yet another Lisp-like, making the point sort of moot since it's part of the definition of being Lisp-like (to many).
--
Macros in particular come up often, but I don't see anything really interesting about them. If you have the full AST available (a rarity, I'll admit), then it seems like a strict subset of runtime modification - it's just a self-eliminating runtime modification, done ahead of time for speed purposes. Except for maybe C-style macros, which (via a horrible syntax) can generate anything, not just valid code, which isn't always possible.
[1]: (setf (sqrt something) number) => (set-sqrt something number) => (setf something (* number number)) in here: http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.h... found via here: http://stackoverflow.com/questions/267862/what-makes-lisp-ma...
[2]: https://www.ruby-forum.com/topic/1108988
Well, the experience of most advanced Lisp uses is totally different.
http://steve-yegge.blogspot.com/2006/04/lisp-is-not-acceptab...
There's no doubt that lisp has its problems, because if not, it would be a lot more popular today than is the case. Do note however, that a lot has changed since Steve posted that blog entry, most notably is the rise of clojure, which offers solutions to a lot of the problems mentioned.