1) A new concept of variables. In Lisp, all variables are effectively pointers. Values are what have types, not variables, and assigning or binding variables means copying pointers, not what they point to.
(defun print-twice (it)
(print it)
(print it))
'it' is a variable. It can be bound to ANY value. There is no restriction and no type associated with the variable. If you call the function, the argument does not need to be copied. The variable is similar to a pointer. It has a way to access the value that is bound to the variable. There is no need to reserve memory. We can pass any data object when we call the function: any size and any type.
The data objects have a 'type' and all data objects can be queried for its 'type'.
(type-of "abc") -> STRING
2) A symbol type. Symbols differ from strings in that you can test equality by comparing a pointer.
A symbol is a data object with a name. Usually the name can be used to find the object:
|This is a Symbol|
this-is-also-a-symbol
(find-symbol "SIN") -> SIN
Since symbols are real data objects, we can test whether they are the same object:
(eq 'sin 'cos) -> NIL
(eq 'sin 'sin) -> T
This allows us for example to write a sentence with symbols:
(defvar *sentence* '(mary called tom to tell him the price of the book))
Now we can count the number of THE in the sentence:
(count 'the *sentence*) -> 2
In Common Lisp symbols not only have a name, but they also can have a value, a function, a property list and a package. So symbols can be used to name variables or functions. The property list is usually used to add meta-data to symbols.
3) A notation for code using trees of symbols.
Lisp uses its basic data structures to represent code.
The list (* 3 2) can be both data and code:
(eval '(* 3 (+ 2 5))) -> 21
(length '(* 3 (+ 2 5))) -> 3
The tree:
CL-USER 8 > (sdraw '(* 3 (+ 2 5)))
[*|*]--->[*|*]--->[*|*]--->NIL
| | |
v v v
* 3 [*|*]--->[*|*]--->[*|*]--->NIL
| | |
v v v
+ 2 5
4) The whole language always available. There is no real distinction between read-time, compile-time, and runtime. You can compile or run code while reading, read or run code while compiling, and read or compile code at runtime.
Lisp provides the functions READ to read data and code from text, LOAD to load code, EVAL to evaluate code, COMPILE to compile code and PRINT to write data and code to text.
These functions are always available. They don't go away. They can be part of any program. That means any program can read, load, eval or print code - always.
How are they different in languages like C or Java?
Those languages don't provide symbols, code as data or runtime evaluation of data as code. Data objects in C are usually untyped.
Do any other languages other than LISP family languages have any of these constructs now?
Many languages have some of these capabilities.
The difference:
In Lisp these capabilities are designed into the language so that they are easy to use.