Object Oriented and Procedural Lisp

Summary: Common Lisp combines object-oriented, functional, and procedural programming within a single, unified system.
prev next

site map



"No single language can support every style, but a variety of styles can be supported within the framework of a single language. Where this can be done, significant benefits arise from sharing a common type system, a common toolset, and so forth. These technical advantages translate into important practical benefits such as enabling groups with moderately differing needs to share a language rather than having to apply a number of specialized languages." - Bjarne Stroustrup

There is no one-true programming methodology:

  • Object Oriented programming groups data into classes and operations on those classes. Conceptually, the "same" operation might do different things, depending on the classes of the data it is being applied to. Typically, classes can be defined in terms of other classes (inheritance), so that the data within a particular instance of a class, and the operations applicable to those instances, can be shared between the new class and the class(es) from which it inherits.

  • Functional programming describes all computer operations as mathematical functions on inputs. Typically, a function can be created and returned from other functions as first-class data. This function object may then be passed as input to other functions, perhaps be composed with other functions, and eventually, applied to inputs to produced a value. Objects can be defined in terms of functions that encapsulate certain data, and operations on objects can be defined by functions encapsulating the objects. Purely functional languages do not have assignment, as all side-effecting can be defined in terms of functions that encapsulate the changed data.

  • Procedural languages essentially perform everything as side-effects to data structures. A purely procedural language would have no functions, but might have "subroutines" of no arguments that returned no values, and performed certain assignments and other operations based on the data it found stored in the system.
Common Lisp provides integrated support for all these methodologies:

  • There are procedural operations such as assignment and many different kinds of iteration and other control flow constructs (even go). Some functions come in two versions: one which side-effects its inputs and one which does not.

  • There is good support for functional programming. In fact, much functional programming technology was first developed using earlier Lisps. Anonymous functions can be produced within other "function factories" and returned as indefinite extent, first-class objects. In particular, a function created in this way has access to the local variables of the function in which it was defined - even if the new function is used after the defining function has already returned. This is what allows the encapsulation of data described earlier.

  • Common Lisp, with the Common Lisp Object System (CLOS), was the first object-oriented programming language to receive an ANSI standard. It is, arguably, the most complete and advanced object system of any programming language. It supports:
    • multiple-inheritance, where classes can inherit from multiple superclasses.
    • multi-method dispatch, where operations can be specialized not just on a single object, but on any of the arguments for the operation, or any combination of those arguments.
    • method combination, in which the means by which several applicable operations to a particular set of arguments can be combined, is itself an object-oriented specification, definable by the programmer.
    • operations that can specialize on particular instances of classes, rather than on just the broad class of the arguments.
    • shared class variables (slots, attributes) that are shared by all instances of a class, in addition to instance variables which are unique to each instance.
    • specification of the creation operation for instances of a class as an object-oriented program, definable by the programmer.
    • The behavior of operations on objects (methods) are themselves defined as first-class objects, which can be specialized by the programmer.
    • Access to classes themselves as first-class objects. The objects which define the behavior of classes are themselves classes, usable directly by the programmer.
    These features, combined with the Metaobject Protocol, make CLOS itself an object-oriented program which can be customized extensively by the programmer.

The Common Lisp support for each of these programming methodologies is generally more complete than languages specialized for just one of the methodologies. What makes Common Lisp truly useful, however, is that these different methodologies have been well integrated with each other, and with other features of Common Lisp. For example:

  • All operations in Lisp are invoked the same way, whether they are side-effecting "procedures", normal functions, or operations specialized on classes. No special syntax is used, and the caller need not be aware of how the operation was defined. In fact, the operation can even be redefined to work differently, and the call will correctly use the new definition. (Unless that is not desirable.)

  • Instance (or shared class) variables of an object can be accessed by class-specialized functions, and called the same way as any other function.

  • The same assignment operator can be used to assign values to local or global variables, to instance (or shared class) variables of an object, or to conceptual "places" that are represented by functions that encapsulate the notion of "place".

  • All operations in Lisp are first class objects and instances of some sort of function class, regardless of whether they are side-effecting "procedures", normal functions, or operations specialized on classes. Each of these function objects can be passed as arguments to other operations, and applied to arguments in the same way.

  • All data in Common Lisp is an instance of some class and can participate in operation specialization (method discrimination) - even functions, characters, and the components of the object system itself.

  • Run-time typing can be used for class discrimination.

  • Every operation in Lisp, whether a normal function, an operation specialized on classes, or even a construction for procedural control flow, always returns something. There are no such things as "subroutines," which cannot be composed in expressions. Thus operations can be combined in a functional way.

  • All operations in Lisp can return multiple values. This encourages functional programming, even within object-oriented programming, by making it unnecessary to side-effect arguments merely because an operation conceptually produces more than one value. The multiple values are just returned. (There's no need for "output arguments".)

  • The dynamic nature of Lisp allows functions, classes, and operations on classes to be redefined or added to. If this results in new instance variables for instances of a class, the new variables are added to old instances with appropriate (and programmer controllable) initial values. If new class definitions or operations on them make a different set of operations applicable to a particular set of arguments in a call, the program which makes the call will be updated automatically to use the new operations.