We knew that everyone else was writing their software in C++ or Perl. But we also knew that that didn't mean anything. If you chose technology that way, you'd be running Windows and Java. When you choose technology, you have to ignore what other people are doing, and consider only what will work the best. - pg.
A language that doesn't affect the way you think about programming, is not worth knowing. - Norvig
All conditioned phenomena
Are like a dream, an illusion, a bubble, a shadow,
Like dew or a flash of lightning;
Thus we shall perceive them.
Developing proper understanding.
For reasons of conceptual integrity we want one uniform mechanism. I don't remember where I picked this phrase, but I like it. It has pseudo scientific pathos in it and an attempt to capture some aspect of Nature.
So, let's simplify and clarify a bit.
First of all, let's unlearn some "wrong" memes. Let's forget the word "object" and use word "value" instead.
Evaluation is a process of "deriving" a value of an expression (a Lisp form).
It is important to note that in Lisp it is values that are typed, not "variables".
Logically, everything is a symbol. There are no variables of different types (boxes of different kinds). Each value has a type tags attached to it. It could "belong to" more than one type.
Lets unlearn the word "variable". At least in the common sense of a "box for values of some type or another".
Lets use a more general (simplified?) notion of Symbols bound to (associated with) Values instead.
Logically, bound Symbols are "Pointers". They could be re-bound.
Thus we have some uniformity and conceptual integrity.
- Uniform references to values, using Symbols.
- Uniform data structure for code and data (everything made out of "conses", every Lisp form).
- Uniform prefix notation (CAR is an operator, CDR is a list of operands) easy to parse/generate code.
Logically, everything is a Symbol. We shall think this way. Data Types are orthogonal to Symbols. It about Values.
"Numbers are provided in several forms and representations". - from Symbolics CL manual.
"Representation" here stands for our "inner representation" of a concepts, part of models of the world we each build in our minds.
Form is what we use to communicate "parts of our models" (concepts) using words of human language as symbols..
This is how we shall think about it.
So, in a Lisp
-1 is a form (a symbol), while representation could be some ordered set of bits of some size.
|'c' 'h' 'a' 'r'||Lisp Symbol||Type-tagged Lisp Value|
|"some string"||Lisp Symbol||Type-tagged Lisp Value|
|144072||Lisp Symbol||Type-tagged Lisp Value|
Representation is not necessarily machine-dependent
REPL displays Symbols is so-called human-readable form. It uses "conventional" chars, strings and numbers, which are not the same as Lisp's inner representation of values of strings and numbers.
Symbols here are for using with so-called human-reasoning or logic. This is why we must know but forget all about "chars" and "strings" and "numbers". There are only Lisp Symbols we deal with.
How values are represented inside a Lisp is Implementation Details. We must know it, but forget for now. Lisp will take care of it, so we could relax.
There is another useful analogy. When we combine symbols into lists, such as
(+ 2 3) or
(length "hello") we are used to call the individual elements of a List "atoms".
The idea here is that, we should think of them as "indivisible", "primitive", most basic "values".
But these elements in a List are not the actual values themselves, these are Symbols which "stands for" ("or refers to") actual values, like words within our mind stands for some concepts.
This is how shall we think about it.
Lisp provides machinery for locating a Symbol, given its name (in form of a string in REPL) - symbol lookup tables. Symbols serve to name, to refer to.
Keywords are symbols which points to "string constants".
Lisp Forms (or expressions) are just Lists of Symbols (made out of "conses").
|(+ 1 2)||Lisp Form - List of Symbols||(cons + (cons 1 (cons 2 nil)))|
This is how we shall think about Lisp expressions.
Data types are set/subset hierarchy of relationships between different kinds of Lisp values.
It is NOT a rigid "is-a" classification. (not some int a; boxes).
It is "kind-of" (set/subset) relationships - typep (a-la instanceOf).
A value could "belong to" more than one type.
Lets not worry about types untill we really confront them. They will be checked by Lisp at runtime. We must know them, but let's not cross the birdge untill we reach it.
"Inner representation behind symbols" have its own structure - property lists, which in effect allow symbols to be treated as record structures with an extensible set of named components, each of which may be any Lisp object.
"Since many Lisp values belong to more than one such set, it doesn't always make sense to ask what the type of an object is; instead, one usually asks only whether a value belongs to a given type."
As long as we have Symbols, everything else follows.
There is a cut from Symbolics CL documentation, because I cannot write better than that.
- Numbers are provided in several forms and representations. Symbolics Common Lisp provides a true integer data type: any integer, positive, or negative, has in principle a representation as a Symbolics Common Lisp data
object, subject only to total memory limitations, rather than to machine word width. A true rational data type is provided: the quotient of two integers, if not an integer, is a ratio. Floating-point numbers of single and double precision are also provided, as well as Cartesian complex numbers.
- Cons is a primitive Lisp data type that consists of a car and a cdr. Linked conses are used to represent a non-empty list.
- Sequences are instances of the sequence type. A sequence is a supertype of the list and vector (one-dimensional array) types. These types have the common property that they are ordered sets of elements. Sequence functions
can be used on either lists or vectors.
- Lists are represented in the form of linked cells called conses. The car of the list is its first element; the cdr is the remainder of the list. There is a special object (the symbol nil) that is the empty list. Lists are built up by
recursive application of their definition.
- Arrays are dimensioned collections of objects. An array can have -a non-negative number of dimensions, up to eight, and is indexed by a sequence of integers. A general array can have any Lisp object as a component; other
types of arrays are specialized for efficiency and can hold only certain types of Lisp objects. It is possible for two arrays, possibly with differing dimension information, to share the same set of elements (such that modifying one array modifies the other also) by causing one to be displaced to the other. One-dimensional arrays of any kind are called vectors. One-dimensional arrays of characters are called strings. One-dimensional array of bits (that is, of integers whose values are 0 or 1) are called bit-vectors.
- Tables provide an efficient way of associating Lisp objects. This is done by associating a key with a value. Some tables are hashed, which is a method for storing the association between the key and the value; this permits faster
association in exchange for some storage overhead.
- Streams represent sources or sinks of data, typically characters or bytes. They are used to perform 1/0, as well as for internal purposes such as parsing strings.
- Structures are user-defined record structures, objects that have named components. The defstruct facility is used to define new structure types. The name of the new structure type becomes a valid type symbol.
- Functions are objects that can be invoked as procedures; these may take arguments and return values. (All Lisp procedures return values and therefore every procedure is a function.) Such objects include compiled-functions (compiled code objects). Some functions are represented as a list whose car is a particular symbol such as lambda. Symbols can also be used as functions.
- Compiled-functions The usual form of compiled, executable code is a Lisp object called a "compiled function." A compiled function contains the code for one function. Compiled functions are produced by the Lisp Compiler and
are usually found as the definitions of symbols. The printed representation of a compiled function includes its name, so that it can be identified. About the only useful thing to do with compiled functions is to apply them to arguments. However, some functions are provided for examining such objects, for user convenience.
- Lexical Closure is a functional object that contains a lexical evaluation environment, for example, an internal lambda in an environment containing lexical variables. These variables can be accessed by the environment of the
internal lambda; the closure is said to be a closure of the free lexical variables. Invocation of a lexical closure provides the necessary data linkage for a function to run in the environment in which the closure was made.
- Dynamic Closure is a functional object that contains a dynamic evaluation environment. Dynamic Closures are created by the closure function and the let-closed special form. Dynamic Closures are closures over special
variables. Invocation of a dynamic closure causes special variables to be bound around the closed-over function.
- Locative is a Lisp object used as a pointer to a single memory cell in the system. Locatives are a low-level construct, and as such, most programmers never use them.
- Packages are collections of symbols that serve as name spaces. The parser recognizes symbols by looking up character sequences in the current package.
- Pathnames represent names of files in a fairly implementation-independent manner. They are used to interface to the external file system.
- Readtables are data structures used to control the parsing of expressions. This structure maps characters into syntax types. This is extensively used by macro characters to read their definitions. You can reprogram the parser
to a limited extent by modifying the read table.
These categories are not always mutually exclusive.
The whole point of this writing is to convey the notion that we should use simplified concept of programming, as just manipulation of Lists of Symbols. Symbolic computation, if you wish.
To do it we should unlearn wrong memes which came from "packer's programming languages".