Yea, right, and this rule also applies to machine code.
And Java programs are just sequences of chars or bytes which can be manipulated in Java. What's your point?
Maybe i didn't explain myself well. I consider recursion and closures a superior programming technique. Its only hard until people get used to it, after that is programming without those techniques that makes it hard. Just imagine handling awt events without inner-classes.
Well, static type checking isn't as easy as you suggest, especially when the types are not specified in the program. Consider this Lisp example:
1 2
| (defun addOne (x) (+ x 1)) |
Since the type of 'x' is not specified this function can be called with an argument of any type, but it will only work for numbers as '+' is not defined for other types (I believe this is the case in Lisp). So, everywhere 'addOne' is used you have to check that the type of the argument to 'x' is valid. This is what advanced programming languages like ML and Haskell does.
You have annotations in lisp almost since the time it was invented. You can attach any structure to any simbol of your program. With this trick its possible to establish a convention for giving types to simbols.
Using your example:
1 2
| (defun addOne (x) (+ x 1)) |
You could expand it to have type info annotated in it. Supose you have two functions to annotate a simbol:
; takes a simbol and returns it
; annotated with several key=value pairs
(put simbol key value ...)
; gets a simbol annotation
(get simbol key)
With this you can now rewrite your function like this:
1 2
| (defun addOne ((put x 'var-type 'integer)) (+ x 1)) |
When you call (type-check my-program) the type-check function has enough information to check your program just like a compiler would.
I think tricks is a bad name and the program you refer to is often called a compiler. The only thing you have accomplished is putting the compiler inside your program.
The main programming technique used in lisp, that is possible with Lisp features, concists in rewriting the language. That is recreate the language for the problem your program is trying to solve. So it makes sense that we write a compiler inside our program.
This may sound a big effort at first but considering the time it takes to write a complex application like a software rendering engine it may be well worth the effort. Most of the complexity of compilers is on the rich sintax languages provide and on the optimization stages. Lisp sintax is so trivial that anyone with bare knowledge of programming could do a parser for it. With annotations you can possibly attach any information a modern compiler does to the abstract syntax tree after parsing the source. So in theory it would be possible to do anything in Lisp that anyother language does with the benefict of being customizable.