Lisp users refer to Lisp as a programmable programming language . It is used for symbolic calculations - calculations with symbols.
Macros are just one way to use the paradigm of symbolic computing. The wider vision is that Lisp provides simple ways to describe symbolic expressions: mathematical terms, logical expressions, iterative statements, rules, descriptions of constraints, and much more. Macros (transformations of the original Lisp forms) are just one application of symbolic computing.
There are some aspects of this: if you ask about the "redefinition" of the language, then a strict redefinition will mean overriding any existing linguistic mechanism (syntax, semantics, pragmatics). But there is also an extension, implementation, removal of language functions.
There have been many attempts in the Lisp tradition to provide these functions. The Lisp dialog and a specific implementation can only offer a subset of them.
Several ways to override / modify / extend the functionality provided by the main implementations of Common Lisp:
s-expression syntax . The syntax of s-expressions is not fixed. The reader (READ function) uses the so-called reading tables to indicate the functions that will be performed when reading the character. You can modify and create reading tables. This allows you, for example, to change the syntax of lists, characters, or other data objects. You can also introduce new syntax for new or existing data types (for example, hash tables). It is also possible to completely replace the s-expression syntax and use a different parsing engine. If the new parser returns Lisp forms, no changes are required for the interpreter or compiler. A typical example is a read macro that can read infix expressions. Inside such a read macro, infix expressions and precedence rules for operators are used. Reading macros are different from regular macros: reading macros is done at the character level of Lisp syntax.
replacement of functions . Top-level functions are tied to characters. User can change this binding. Most implementations have a mechanism that allows this even for many built-in functions. If you want to provide an alternative to the built-in ROOM function, you can replace its definition. Some implementations will result in an error, and then offer an option to continue the change. Sometimes you need to unlock the package. This means that functions can generally be replaced with new definitions. There are limitations to this. First, the compiler can embed functions in code. To see the effect, you need to recompile the code that uses the modified code.
recommendations . Often you want to add some kind of behavior to functions. This is called "counseling" in the Lisp world. Many common Lisp implementations will provide such a facility.
custom packages . Packages group characters in namespaces. The COMMON-LISP package is home to all characters that are part of the ANSI Common Lisp standard. A programmer can create new packages and import existing characters. Thus, you can use the EXTENDED-COMMON-LISP package in your programs, which provides more or more features. Just by adding (IN-PACKAGE "EXTENDED-COMMON-LISP"), you can start development using your extended version of Common Lisp. Depending on the namespace used, the Lisp dialect that you use may look slightly or even radically different. There are several Lisp dialects in Genera on the Lisp machine: ZetaLisp, CLtL1, ANSI Common Lisp, and Symbolics Common Lisp.
CLOS and dynamic objects. Generic Lisp Object System comes with a built-in input. The Meta-Object protocol extends these capabilities. CLOS itself can be extended / redefined in CLOS. You need another inheritance. Write a method. You need different ways to store instances. Write a method. Slots should have more information. Provide a class for this. CLOS itself is designed in such a way that it is able to implement a whole "region" of various object-oriented programming languages. Typical examples are adding things like prototypes, integrating with other people's object systems (like Objective-C), adding resilience, ...
Lisp forms , Interpretation of Lisp forms can be overridden by macros. A macro can analyze the source code that it contains and modify it. There are various ways to control the transformation process. Complex macros use a code walker that understands the syntax of Lisp forms and can apply transformations. Macros can be trivial, but can also be very complex, like LOOP or ITERATE macros. Other typical examples are macros for embedded SQL and embedded HTML generation. Macros can also be used to move computations to compile time. Since the compiler itself is a Lisp program, arbitrary computation can be performed at compile time. For example, a Lisp macro could calculate an optimized version of a formula if certain parameters are known at compile time.
Symbols Generic Lisp contains character macros. Character macros allow you to change the meaning of characters in the source code. A typical example is the following: (with slots (foo) bar (+ foo 17)) Here, the FOO symbol in the source enclosed with WITH-SLOTS will be replaced by a call (the value string of the word "foo").
optimization , with the so-called compiler macros, you can provide more efficient versions of some functions. The compiler will use these compiler macros. This is an effective way for the user to optimize programs.
Handling conditions - handle conditions that arise due to the use of a programming language in a certain way. Generic Lisp provides an advanced way to handle errors. A condition system can also be used to redefine language functions. For example, you can handle errors of the undefined function using autorun autoloading mechanism. Instead of sending it to the debugger, when the undefined function is visible to Lisp, the error handler may try to autoload the function and repeat the operation after loading the necessary code.
Special Variables - Inserts binding variables into existing code. Many Lisp dialects, such as Common Lisp, provide special / dynamic variables. Their value is viewed at runtime on the stack. This allows you to include code to add variable bindings that affect existing code without changing it. A typical example is a variable of type * standard-output *. You can rebuild a variable, and all output using this variable during the dynamic region of the new binding will move in a new direction. Richard Stallman argued that it was very important for him that he was installed by default in Emacs Lisp (although Stallman knew about lexical binding in Scheme and Common Lisp).
Lisp has these and other features, as it is used to implement many different languages and programming paradigms. A typical example is a built-in implementation of a logical language, such as Prolog. Lisp allows you to describe Prolog terms with s-expressions and with a special compiler, Prolog terms can be compiled into Lisp code. Sometimes the usual Prolog syntax is required, then the parser will parse the typical Prolog terms in Lisp forms, which will then be compiled. Other examples for embedded languages are rule-based languages, mathematical expressions, SQL terms, Lisp inline assembler, HTML, XML, and many others.
Rainer Joswig Feb 22 2018-10-22 14:26
source share