- Order:
- Duration: 10:59
- Published: 08 Sep 2008
- Uploaded: 29 May 2011
- Author: ignousocis
Name | Lisp |
---|---|
Paradigm | multi-paradigm: functional, procedural, reflective, meta |
Year | 1958 |
Designer | John McCarthy |
Developer | Steve Russell, Timothy P. Hart, and Mike Levin| latest release version = |
Turing-complete | Yes |
Typing | dynamic, strong |
Dialects | Common Lisp, Scheme, Emacs Lisp, AutoLISP, Logo, Clojure, ISLISP, Newlisp, Arc, SKILL, Racket |
Influenced by | IPL |
Influenced | ML, Perl, Python, Smalltalk, Ruby, Dylan, Haskell, Mathematica, Rebol, Qi, Lua, JavaScript, Forth, Nu, OPS5, CLU, Falcon, Io, Ioke, MDL |
Lisp (or LISP) is a family of computer programming languages with a long history and a distinctive, fully parenthesized syntax. Originally specified in 1958, Lisp is the second-oldest high-level programming language in widespread use today; only Fortran is older (by one year). Like Fortran, Lisp has changed a great deal since its early days, and a number of dialects have existed over its history. Today, the most widely known general-purpose Lisp dialects are Common Lisp, Scheme, and Clojure.
Lisp was originally created as a practical mathematical notation for computer programs, influenced by the notation of Alonzo Church's lambda calculus. It quickly became the favored programming language for artificial intelligence (AI) research. As one of the earliest programming languages, Lisp pioneered many ideas in computer science, including tree data structures, automatic storage management, dynamic typing, and the self-hosting compiler.
The name LISP derives from "LISt Processing". Linked lists are one of Lisp languages' major data structures, and Lisp source code is itself made up of lists. As a result, Lisp programs can manipulate source code as a data structure, giving rise to the macro systems that allow programmers to create new syntax or even new domain-specific languages embedded in Lisp.
The interchangeability of code and data also gives Lisp its instantly recognizable syntax. All program code is written as s-expressions, or parenthesized lists. A function call or syntactic form is written as a list with the function or operator's name first, and the arguments following; for instance, a function f that takes three arguments might be called using (f arg1 arg2 arg3)
.
Information Processing Language was the first AI language, from 1955 or 1956, and already included many of the concepts, such as list-processing and recursion, which came to be used in Lisp.
McCarthy's original notation used bracketed "M-expressions" that would be translated into S-expressions. As an example, the M-expression
is equivalent to the S-expression (car (cons A B))
. Once Lisp was implemented, programmers rapidly chose to use S-expressions, and M-expressions were abandoned. M-expressions surfaced again with short-lived attempts of MLISP by Horace Enea and CGOL by Vaughan Pratt.
Lisp was first implemented by Steve Russell on an IBM 704 computer. Russell had read McCarthy's paper, and realized (to McCarthy's surprise) that the Lisp eval function could be implemented in machine code. The result was a working Lisp interpreter which could be used to run Lisp programs, or more properly, 'evaluate Lisp expressions.'
Two assembly language macros for the IBM 704 became the primitive operations for decomposing lists: car
(Contents of the Address part of Register number) and cdr
(Contents of the Decrement part of Register number). From the context, it is clear that the term "Register" is used here to mean "Memory Register", nowadays called "Memory Location". Lisp dialects still use car
and cdr
( and ) for the operations that return the first item in a list and the rest of the list respectively.
The first complete Lisp compiler, written in Lisp, was implemented in 1962 by Tim Hart and Mike Levin at MIT. This compiler introduced the Lisp model of incremental compilation, in which compiled and interpreted functions can intermix freely. The language used in Hart and Levin's memo is much closer to modern Lisp style than McCarthy's earlier code.
Lisp was a difficult system to implement with the compiler techniques and stock hardware of the 1970s. Garbage collection routines, developed by then-MIT graduate student Daniel Edwards, made it practical to run Lisp on general-purpose computing systems, but efficiency was still a problem. This led to the creation of Lisp machines: dedicated hardware for running Lisp environments and programs. Advances in both computer hardware and compiler technology soon made Lisp machines obsolete.
During the 1980s and 1990s, a great effort was made to unify the work on new Lisp dialects (mostly successors to Maclisp like ZetaLisp and NIL (New Implementation of Lisp)) into a single language. The new language, Common Lisp, was somewhat compatible with the dialects it replaced (the book Common Lisp the Language notes the compatibility of various constructs). In 1994, ANSI published the Common Lisp standard, "ANSI X3.226-1994 Information Technology Programming Language Common Lisp."
Differences between dialects may be quite visible—for instance, Common Lisp and Scheme use different keywords to define functions. Within a dialect that is standardized, however, conforming implementations support the same core language, but with different extensions and libraries.
For more information about various dialects of Lisp see category Lisp programming language family, and The History of Lisp.
Many new Lisp programmers were inspired by writers such as Paul Graham and Eric S. Raymond to pursue a language others considered antiquated. New Lisp programmers often describe the language as an eye-opening experience and claim to be substantially more productive than in other languages. This increase in awareness may be contrasted to the "AI winter" and Lisp's brief gain in the mid-1990s.
Dan Weinreb lists in his survey of Common Lisp implementations eleven actively maintained Common Lisp implementations. Scieneer Common Lisp is a new commercial implementation forked from CMUCL with a first release in 2002.
The open source community has created new supporting infrastructure: Cliki is a wiki that collects Common Lisp related information, the Common Lisp directory lists resources, #lisp is a popular IRC channel (with support by a Lisp-written Bot), lisppaste supports the sharing and commenting of code snippets, Planet Lisp collects the contents of various Lisp-related Blogs, on LispForum user discuss Lisp topics, Lispjobs is a service for announcing job offers and there is a new weekly news service (Weekly Lisp News). Common-lisp.net is a hosting site for open source Common Lisp projects.
50 years of Lisp (1958–2008) has been celebrated at LISP50@OOPSLA. There are several regular local user meetings (Boston, Vancouver, Hamburg, ...), Lisp Meetings (European Common Lisp Meeting, European Lisp Symposium) and an International Lisp Conference.
The Scheme community actively maintains over twenty implementations. Several significant new implementations (Chicken, Gambit, Gauche, Ikarus, Larceny, Ypsilon) have been developed in the last few years. The Revised5 Report on the Algorithmic Language Scheme standard of Scheme was widely accepted in the Scheme community. The Scheme Requests for Implementation process has created a lot of quasi standard libraries and extensions for Scheme. User communities of individual Scheme implementations continue to grow. A new language standardization process was started in 2003 and led to the R6RS Scheme standard in 2007. Academic use of Scheme for teaching computer science seems to have declined somewhat. Some universities are no longer using Scheme in their computer science introductory courses.
There are several new dialects of Lisp: Arc, Nu, and Clojure.
Common Lisp is a successor to MacLisp. The primary influences were Lisp Machine Lisp, MacLisp, NIL, S-1 Lisp, Spice Lisp, and Scheme. It has many of the features of Lisp Machine Lisp (a large Lisp dialect used to program Lisp Machines), but was designed to be efficiently implementable on any personal computer or workstation. Common Lisp has a large language standard including many built-in data types, functions, macros and other language elements, as well as an object system (Common Lisp Object System or shorter CLOS). Common Lisp also borrowed certain features from Scheme such as lexical scoping and lexical closures.
Scheme (designed earlier) is a more minimalist design, with a much smaller set of standard features but with certain implementation features (such as tail-call optimization and full continuations) not necessarily found in Common Lisp.
Scheme is a statically scoped and properly tail-recursive dialect of the Lisp programming language invented by Guy Lewis Steele Jr. and Gerald Jay Sussman. It was designed to have an exceptionally clear and simple semantics and few different ways to form expressions. A wide variety of programming paradigms, including imperative, functional, and message passing styles, find convenient expression in Scheme. Scheme continues to evolve with a series of standards (Revisedn Report on the Algorithmic Language Scheme) and a series of Scheme Requests for Implementation.
Clojure is a dynamic programming dialect of Lisp that targets the Java Virtual Machine (and the CLR ). It is designed to be a general-purpose language, combining the approachability and interactive development of a scripting language with an efficient and robust infrastructure for multithreaded programming. Clojure is a compiled language, as it compiles directly to JVM bytecode, yet remains completely dynamic. Every feature supported by Clojure is supported at runtime. Clojure provides easy access to the Java frameworks, with optional type hints and type inference, to ensure that calls to Java can avoid reflection.
In addition, Lisp dialects are used as scripting languages in a number of applications, with the most well-known being Emacs Lisp in the Emacs editor, Visual Lisp in AutoCAD, Nyquist in Audacity. The small size of a minimal but useful Scheme interpreter makes it particularly popular for embedded scripting. Examples include SIOD and TinyScheme, both of which have been successfully embedded in the GIMP image processor under the generic name "Script-fu". LIBREP, a Lisp interpreter by John Harper originally based on the Emacs Lisp language, has been embedded in the Sawfish window manager. The Guile interpreter is used in GnuCash. Within GCC, the MELT plugin provides a Lisp-y dialect, translated into C, to extend the compiler by coding additional passes (in MELT).
The ubiquitous if-then-else structure, now taken for granted as an essential element of any programming language, was invented by McCarthy for use in Lisp, where it saw its first appearance in a more general form (the cond structure). It was inherited by ALGOL, which popularized it.
Lisp deeply influenced Alan Kay, the leader of the research on Smalltalk, and then in turn Lisp was influenced by Smalltalk, by adopting object-oriented programming features (classes, instances, etc.) in the late 1970s.
Largely because of its resource requirements with respect to early computing hardware (including early microprocessors), Lisp did not become as popular outside of the AI community as Fortran and the ALGOL-descended C language. Newer languages such as Java and Python have incorporated some limited versions of some of the features of Lisp, but are necessarily unable to bring the coherence and synergy of the full concepts found in Lisp. Because of its suitability to ill-defined, complex, and dynamic applications, Lisp is currently enjoying some resurgence of popular interest.
McCarthy's 1958 paper introduced two types of syntax: S-expressions (Symbolic expressions, also called "sexps"), which mirror the internal representation of code and data; and M-expressions (Meta Expressions), which express functions of S-expressions. M-expressions never found favor, and almost all Lisps today use S-expressions to manipulate both code and data.
The use of parentheses is Lisp's most immediately obvious difference from other programming language families. As a result, students have long given Lisp nicknames such as Lost In Stupid Parentheses, or Lots of Irritating Superfluous Parentheses. However, the S-expression syntax is also responsible for much of Lisp's power: the syntax is extremely regular, which facilitates manipulation by computer. However, the syntax of Lisp is not limited to traditional parentheses notation. It can be extended to include alternative notations. XMLisp, for instance, is a Common Lisp extension that employs the metaobject-protocol to integrate S-expressions with the Extensible Markup Language (XML).
The reliance on expressions gives the language great flexibility. Because Lisp functions are themselves written as lists, they can be processed exactly like data. This allows easy writing of programs which manipulate other programs (metaprogramming). Many Lisp dialects exploit this feature using macro systems, which enables extension of the language almost without limit.
(1 2 foo)
is a list whose elements are three atoms: the values 1
, 2
, and foo
. These values are implicitly typed: they are respectively two integers and a Lisp-specific data type called a "symbol", and do not have to be declared as such.
The empty list ()
is also represented as the special atom nil
. This is the only entity in Lisp which is both an atom and a list.
Expressions are written as lists, using prefix notation. The first element in the list is the name of a form, i.e., a function, operator, macro, or "special operator" (see below.) The remainder of the list are the arguments. For example, the function list
returns its arguments as a list, so the expression
evaluates to the list (1 2 foo)
. The "quote" before the arguments in the preceding example is a "special operator" which prevents the quoted arguments from being evaluated (not strictly necessary for the numbers, since 1 evaluates to 1, etc.). Any unquoted expressions are recursively evaluated before the enclosing expression is evaluated. For example,
evaluates to the list (1 2 (3 4))
. Note that the third argument is a list; lists can be nested.
1 + 2 + 3 + 4
". Arithmetic operators in Lisp are variadic (or n-ary), able to take any number of arguments.
"Special operators" (sometimes called "special forms") provide Lisp's control structure. For example, the special operator if
takes three arguments. If the first argument is non-nil, it evaluates to the second argument; otherwise, it evaluates to the third argument. Thus, the expression
evaluates to (3 4 "bar")
. Of course, this would be more useful if a non-trivial expression had been substituted in place of nil
.
lambda
, is used to bind variables to values which are then evaluated within an expression. This operator is also used to create functions: the arguments to lambda
are a list of arguments, and the expression or expressions to which the function evaluates (the returned value is the value of the last expression that is evaluated). The expression
evaluates to a function that, when applied, takes one argument, binds it to arg
and returns the number one greater than that argument. Lambda expressions are treated no differently from named functions; they are invoked the same way. Therefore, the expression
evaluates to 6
.
(FOO (BAR 1) 2)
contains three elements: the symbol FOO, the list (BAR 1)
, and the number 2.
The essential difference between atoms and lists was that atoms were immutable and unique. Two atoms that appeared in different places in source code but were written in exactly the same way represented the same object, whereas each list was a separate object that could be altered independently of other lists and could be distinguished from other lists by comparison operators.
As more data types were introduced in later Lisp dialects, and programming styles evolved, the concept of an atom lost importance. Many dialects still retained the predicate atom for legacy compatibility, defining it true for any object which is not a cons.
data
and next
fields discussed in the article linked list, respectively.
Of the many data structures that can be built out of cons cells, one of the most basic is called a proper list. A proper list is either the special nil
(empty list) symbol, or a cons in which the car
points to a datum (which may be another cons structure, such as a list), and the cdr
points to another proper list.
If a given cons is taken to be the head of a linked list, then its car points to the first element of the list, and its cdr points to the rest of the list. For this reason, the car
and cdr
functions are also called first
and rest
when referring to conses which are part of a linked list (rather than, say, a tree).
Thus, a Lisp list is not an atomic object, as an instance of a container class in C++ or Java would be. A list is nothing more than an aggregate of linked conses. A variable which refers to a given list is simply a pointer to the first cons in the list. Traversal of a list can be done by "cdring down" the list; that is, taking successive cdrs to visit each cons of the list; or by using any of a number of higher-order functions to map a function over a list.
Because conses and lists are so universal in Lisp systems, it is a common misconception that they are Lisp's only data structures. In fact, all but the most simplistic Lisps have other data structures – such as vectors (arrays), hash tables, structures, and so forth.
(a . b)
, where a
is the car and b
the cdr. A longer proper list might be written (a . (b . (c . (d . nil))))
in dotted-pair notation. This is conventionally abbreviated as (a b c d)
in list notation. An improper list may be written in a combination of the two – as (a b c . d)
for the list of three conses whose last cdr is d
(i.e., the list (a . (b . (c . d)))
in fully specified form).
list
procedure, which takes any number of arguments, and returns the list of these arguments.
Because of the way that lists are constructed from cons pairs, the cons
procedure can be used to add an element to the front of a list. Note that the cons
procedure is asymmetric in how it handles list arguments, because of how lists are constructed.
The append
procedure appends two (or more) lists to one another. Because Lisp lists are linked lists, appending two lists has asymptotic time complexity
foo
and bar
are (a b c)
and (x b c)
respectively. However, the tail (b c)
is the same structure in both lists. It is not a copy; the cons cells pointing to b
and c
are in the same memory locations for both lists.
Sharing structure rather than copying can give a dramatic performance improvement. However, this technique can interact in undesired ways with functions that alter lists passed to them as arguments. Altering one list, such as by replacing the c
with a goose
, will affect the other:
This changes foo
to (a b goose)
, but thereby also changes bar
to (x b goose)
– a possibly unexpected result. This can be a source of bugs, and functions which alter their arguments are documented as destructive for this very reason.
Aficionados of functional programming avoid destructive functions. In the Scheme dialect, which favors the functional style, the names of destructive functions are marked with a cautionary exclamation point, or "bang"—such as set-car!
(read set car bang), which replaces the car of a cons. In the Common Lisp dialect, destructive functions are commonplace; the equivalent of set-car!
is named rplaca
for "replace car." This function is rarely seen however as Common Lisp includes a special facility, setf
, to make it easier to define and use destructive functions. A frequent style in Common Lisp is to write code functionally (without destructive calls) when prototyping, then to add destructive calls as an optimization where it is safe to do so.
(+ 2 3)
evaluates to 5
. However, most other forms evaluate to themselves: if you enter 5
into Lisp, it returns 5
.
Any expression can also be marked to prevent it from being evaluated (as is necessary for symbols and lists). This is the role of the quote
special operator, or its abbreviation '
(a single quotation mark). For instance, usually if you enter the symbol foo
you will get back the value of the corresponding variable (or an error, if there is no such variable). If you wish to refer to the literal symbol, you enter (quote foo)
or, usually, 'foo
.
Both Common Lisp and Scheme also support the backquote operator (known as quasiquote in Scheme), entered with the `
character. This is almost the same as the plain quote, except it allows expressions to be evaluated and their values interpolated into a quoted list with the comma and comma-at operators. If the variable snue
has the value (bar baz)
then `(foo ,snue)
evaluates to (foo (bar baz))
, while `(foo ,@snue)
evaluates to (foo bar baz)
. The backquote is most frequently used in defining macro expansions.
Self-evaluating forms and quoted forms are Lisp's equivalent of literals. It may be possible to modify the values of (mutable) literals in program code. For instance, if a function returns a quoted form, and the code that calls the function modifies the form, this may alter the behavior of the function on subsequent iterations.
(let ((stuff (should-be-constant))) (setf (third stuff) 'bizarre)) ; bad!
(should-be-constant) ; returns (one two bizarre)
Modifying a quoted form like this is generally considered bad style, and is defined by ANSI Common Lisp as erroneous (resulting in "undefined" behavior in compiled files, because the file-compiler can coalesce similar constants, put them in write-protected memory, etc.).
Lisp's formalization of quotation has been noted by Douglas Hofstadter (in Gödel, Escher, Bach) and others as an example of the philosophical idea of self-reference.
Lisp macros operate on these structures. Because Lisp code has the same structure as lists, macros can be built with any of the list-processing functions in the language. In short, anything that Lisp can do to a data structure, Lisp macros can do to code. In contrast, in most other languages, the parser's output is purely internal to the language implementation and cannot be manipulated by the programmer. Macros in C, for instance, operate on the level of the preprocessor, before the parser is invoked, and cannot re-structure the program code in the way Lisp macros can.
In simplistic Lisp implementations, this list structure is directly interpreted to run the program; a function is literally a piece of list structure which is traversed by the interpreter in executing it. However, most actual Lisp systems (including all conforming Common Lisp systems) also include a compiler. The compiler translates list structure into machine code or bytecode for execution.
The basic operation of the REPL is as follows. This is a simplistic description which omits many elements of a real Lisp, such as quoting and macros.
The read
function accepts textual S-expressions as input, and parses them into an internal data structure. For instance, if you type the text (+ 1 2)
at the prompt, read
translates this into a linked list with three elements: the symbol +
, the number 1, and the number 2. It so happens that this list is also a valid piece of Lisp code; that is, it can be evaluated. This is because the car of the list names a function—the addition operation.
Note that a foo
will be read as a single symbol. 123
will be read as the number 123. "123"
will be read as the string "123".
The eval
function evaluates the data, returning zero or more other Lisp data as a result. Evaluation does not have to mean interpretation; some Lisp systems compile every expression to native machine code. It is simple, however, to describe evaluation as interpretation: To evaluate a list whose car names a function, eval
first evaluates each of the arguments given in its cdr, then applies the function to the arguments. In this case, the function is addition, and applying it to the argument list (1 2)
yields the answer 3
. This is the result of the evaluation.
The symbol foo
evaluates to the value of the symbol foo. Data like the string "123" evaluates to the same string. The list (quote (1 2 3))
evaluates to the list (1 2 3).
It is the job of the print
function to represent output to the user. For a simple result such as 3
this is trivial. An expression which evaluated to a piece of list structure would require that print
traverse the list and print it out as an S-expression.
To implement a Lisp REPL, it is necessary only to implement these three functions and an infinite-loop function. (Naturally, the implementation of eval
will be complicated, since it must also implement all special operators like if
or lambda
.) This done, a basic REPL itself is but a single line of code: (loop (print (eval (read))))
.
The Lisp REPL typically also provides input editing, an input history, error handling and an interface to the debugger.
Lisp is usually evaluated eagerly. In Common Lisp, arguments are evaluated in applicative order ('leftmost innermost'), while in Scheme order of arguments is undefined, leaving room for optimization by a compiler.
cond
, is the precursor to later if-then-else
structures.)
Programmers in the Scheme dialect often express loops using tail recursion. Scheme's commonality in academic computer science has led some students to believe that tail recursion is the only, or the most common, way to write iterations in Lisp, but this is incorrect. All frequently seen Lisp dialects have imperative-style iteration constructs, from Scheme's do
loop to Common Lisp's complex loop
expressions. Moreover, the key issue that makes this an objective rather than subjective matter is that Scheme makes specific requirements for the handling of tail calls, and consequently the reason that the use of tail recursion is generally encouraged for Scheme is that the practice is expressly supported by the language definition itself. By contrast, ANSI Common Lisp does not require the optimization commonly referred to as tail call elimination. Consequently, the fact that tail recursive style as a casual replacement for the use of more traditional iteration constructs (such as do
, dolist
or loop
) is discouraged in Common Lisp is not just a matter of stylistic preference, but potentially one of efficiency (since an apparent tail call in Common Lisp may not compile as a simple jump) and program correctness (since tail recursion may increase stack use in Common Lisp, risking stack overflow).
Some Lisp control structures are special operators, equivalent to other languages' syntactic keywords. Expressions using these operators have the same surface appearance as function calls, but differ in that the arguments are not necessarily evaluated—or, in the case of an iteration expression, may be evaluated more than once.
In contrast to most other major programming languages, Lisp allows the programmer to implement control structures using the language itself. Several control structures are implemented as Lisp macros, and can even be macro-expanded by the programmer who wants to know how they work.
Both Common Lisp and Scheme have operators for non-local control flow. The differences in these operators are some of the deepest differences between the two dialects. Scheme supports re-entrant continuations using the call/cc
procedure, which allows a program to save (and later restore) a particular place in execution. Common Lisp does not support re-entrant continuations, but does support several ways of handling escape continuations.
Frequently, the same algorithm can be expressed in Lisp in either an imperative or a functional style. As noted above, Scheme tends to favor the functional style, using tail recursion and continuations to express control flow. However, imperative style is still quite possible. The style preferred by many Common Lisp programmers may seem more familiar to programmers used to structured languages such as C, while that preferred by Schemers more closely resembles pure-functional languages such as Haskell.
Because of Lisp's early heritage in list processing, it has a wide array of higher-order functions relating to iteration over sequences. In many cases where an explicit loop would be needed in other languages (like a for
loop in C) in Lisp the same task can be accomplished with a higher-order function. (The same is true of many functional programming languages.)
A good example is a function which in Scheme is called map
and in Common Lisp is called mapcar
. Given a function and one or more lists, mapcar
applies the function successively to the lists' elements in order, collecting the results in a new list:
This applies the +
function to each corresponding pair of list elements, yielding the result (11 22 33 44 55)
.
The basic "Hello world" program: As the reader may have noticed from the above discussion, Lisp syntax lends itself naturally to recursion. Mathematical problems such as the enumeration of recursively defined sets are simple to express in this notation.
Evaluate a number's factorial:
An alternative implementation, often faster than the previous version if the Lisp system has tail recursion optimization:
Contrast with an iterative version which uses Common Lisp's loop
macro:
The following function reverses a list. (Lisp's built-in reverse function does the same thing.)
Category:Lisp programming language Category:Lisp programming language family Category:1958 in computer science Category:Programming languages created in the 1950s Category:Articles with example Lisp code Category:Academic programming languages Category:Dynamically typed programming languages Category:Functional languages Category:American inventions
This text is licensed under the Creative Commons CC-BY-SA License. This text was originally published on Wikipedia and was developed by the Wikipedia community.