Common LISP
From DesigningPatterns
Contents |
Background
Common Lisp is a Lisp dialect that was standardized by ANSI in 1999. It includes features from a number of earlier Lisp dialects. It supports imperative, functional, and object-oriented programming. Ruby strongly was influenced by Lisp, and in fact is an attempt to create a simpler, pre-Common Lisp.
Characteristics
General
- Common LISP is not a purely functional language; Scheme is far more purely functional, although not as much as Haskell and ML.
- Common LISP is a dynamically-typed language.
- Everything in Common Lisp is an expression (with a return value), just as in Ruby.
- A powerful tool called the REPL (read, evaluate, and print loop) is included with Common Lisp implementations. This allows you to enter, compile, and run code interactively (similar to, but more powerful than, Ruby's irb).
- Lisp pioneered heap allocation and garbage collection.
- Common Lisp code consists of a series of s-expressions (parenthesized lists and atoms).
- Lisp compilers first convert source code into a series of s-expressions and then interpret the s-expressions as forms.
- Lisp has a literal syntax for ratios.
- Common Lisp is case sensitive, although the reader converts all unescaped characters in a symbol to uppercase.
- Writing all code in lowercase is a standard Common Lisp coding convention.
- Separating words in variables with hyphens is a standard Common Lisp naming convention.
- Surrounding global variables with asterisks is a standard Common Lisp naming convention, in order to distinguish dynamically bound variables from lexically bound variables.
- Surrounding constants with pluses is a standard Common Lisp naming convention.
- Common Lisp allows an usually wide range of characters to be used in identifiers, including hyphens. Technically, any character can be used in an identifier if it is escaped (either with "\" or by surrounding the escaped portion of the identifier with vertical bars).
- All atoms are self-evaluating Lisp forms, except for symbols which evaluate to whatever they reference (although there are self-evaluating symbols that reference storage that contains the symbol).
- List Lisp forms must be function call forms, macro forms, or special forms.
- Function calls are evaluated by evaluating all elements of the list form after the initial symbol and passing these evaluated elements as arguments to the function named by the symbol.
- Special forms exist because some operations, such as conditional evaluation, cannot be expressed with function call forms (because, in the function call form, all list elements are evaluated).
- Macro forms accept s-expressions (which are passed, unevaluated, to the macro) and return Lisp forms, which then automatically are evaluated. The first pass (the conversion of s-expressions to a Lisp form) happens at compile-time; the evaluation happens at run-time. Like special forms, macros allow operations that cannot be expressed as function calls but, unlike special forms, can be user-defined.
- Common Lisp is a Lisp-2 (there are two namespaces, one for variables and one for operators), whereas Scheme is a LISP-1.
- Everything can be used as a true value in Common Lisp, exception for
nil.-
niland the empty list (()) are interchangeable. -
tis the canonical true - Scheme has distinct
nil,#f(false), and()values.
-
- Common Lisp has four different built-in equality functions.
-
EQreturns true if and only if the arguments are the same object. Two integer literals may or may not be the same object for the purposes ofEQ, depending on the implementation. -
EQLis the same asEQ, except that number and characters always are compared by content. -
EQUALis just likeeql, except that it will compare the contents of strings and lists. -
EQUALPis just likeEQUAL, except that it does case-insensitive comparison for strings and characters.
-
Variables
- A variable binding in Common LISP is the storage associated with a variable, which contains a reference to the variable's value.
- C++ variables, by default, are associated with storage that contains the a variable's value, not a reference to the variable's value. Of course, it is possible to explicitly specify Common LISP's extra level of indirection by storing addresses (pointer variables) or references.
- Java variables store values for primitives and references for instances.
- Ruby variables always store references, just like those of Common LISP.
- A variable in Common LISP actually can have a stack of bindings, with each new stack level being created by the use of a binding form (the
letspecial operator and arguments being bound to function parameters are examples)- The the binding at the top of the stack always shadows all bindings lower in the stack.
- Code in a given context only can change the binding at the top of the stack (so, once execution leaves the context in which the top of the stack is defined, the stack will be popped and any changes to the binding will be lost). Of course, any modifications to the variable's value will not be undone (just changes to the binding, to what the variable referred).
- Common LISP supports two kinds of variables, lexical variables and special variables.
- A binding for lexical variables is valid within the lexical scope of the binding form.
- A binding for special variables is valid within the dynamic extent of the binding form (dynamic scope means everything executed until execution leaves the binding form's scope, including in any functions called from within the scope).
- Special variables usually are global variables. It is critically important to following the global variable naming convention of adding
*characters before and after the variable name, so as to eliminate any chance of inadvertently adding a binding to a global variable (using the same name for a function parameter and a global variable, for instance).
Functions
- LISP implements many operations with function calls (such as the arithmetic operations) that are implemented with compiler-supplied constructs in other languages. The use of the function call form for arithmetic operations (which is equivalent to prefix notation) eliminates any need to talk about operator precedence.
- Common LISP supports required, optional, variadic, and keyword arguments.
- Full support is provided for optional parameters. In particular, any number of the final arguments can be optional. Default values can be specified for optional parameters (otherwise the default value is NIL), which only will be evaluated if necessary. Default values can depend on the values of earlier parameters and, if desired, an additional parameter (named with a trailing "supplied-p" by convention) will be set with a boolean value for whether or not the argument was specified by the caller.
- Variadic parameters (called "rest" parameters in Common LISP) can be specified after optional parameters. During a call, the parameter is bound to a list containing the additional arguments. The
+function in Common LISP is variadic (which is very unusual for the addition function or operator in a language). - Keyword parameters (also known as "named parameters") allow arguments to be specified by name in any order, in contrast to the required, optional, and variadic parameters that are positional (arguments are bound to positional parameters solely based on the order in which they are specified in the call's argument list). As with optional parameters, default values can be specified for keyword parameters, and optional supplied-p predicates to test whether an argument was supplied for a keyword parameter can be specified. A keyword parameter can be referred to by a different name within a function than the one used to specify arguments in calls of the function.
- Ruby does not provide keyword parameters, but its collecting any hash mappings at the end of argument lists into single hash parameters provides a similar mechanism. This trick is inferior to the language support provided by Common LISP, however, because no checking of parameters is done by the interpreter (it is very possible to misspell a keyword name in the call, which will not be caught by the interpreter but which will result in the intended parameter not getting a value). Additionally, Ruby's trick does not provide any support for specifying default values (let alone supplied-p predicates).
- Required, optional, variadic, and keyword parameters all can be used within the same function.
- Using optional and keyword parameters together is dangerous, because if any of the optional parameters do not have arguments specified, they will consume keyword parameter components (keyword symbols and arguments).
- If variadic and keyword parameters are used within the same function, then the trailing arguments will be packaged twice, once as variadic parameters (including the keyword symbols) and once as keyword parameters.
- Just as in Ruby, the last expression evaluated in a function becomes the return value of the function.
- The
return-fromspecial operator is used to return from functions. This operator is not as clean as the typicalreturnstatement in that a function name always must be specified, even when returning from the current function (thus, the current function name is duplicated in this operator's argument list). - The
functionspecial operator (which can be abbreviated with#') returns an object representing the specified function, which then can be passed around and invoked (higher-order functions).- Ruby offers higher-order functions through blocks and instances of the
MethodandUnboundMethodclasses.
- Ruby offers higher-order functions through blocks and instances of the
- Anonymous functions (lambdas) are closures, just like Ruby blocks.
Macros
- Common Lisp contains powerful macro functionality.
- One key to a Lisp macro is that the arguments are not evaluated but instead passed "as-is" to the macro.
- The other key to a Lisp macro is that they, unlike C++ macros, can make full use of Lisp.
- Common LISP contains no special operators that provide looping functionality; all loops are macros implemented on top of the
tagbodyandgospecial operators (which implementgoto).
Customization
Designing Patterns uses sbcl. Weirdly enough, sbcl does not offer binary distributions of the latest version for the most common Linux architectures (i386 + x86_64). In order to get the latest version, download the most recent binary distribution and install it in a home directory with:
INSTALL_ROOT=~/Lisp sh install.sh export SBCL_HOME=~/Lisp/lib/sbcl
Then, download the latest source and build it; the build will utilize the installed earlier sbcl version.
SLIME should be installed for emacs.
References
Research Points
- Code documentation (the function documentation system, any doxygen equivalent, etc.)
- Symbol macros
- Reader macros
- Local, special variables.
