Scheme's philosophy (from the Revised(5) Report on the Algorithmic Language Scheme
Programming languages should be designed not by piling feature on top of feature, but by removing the weakenesses and restrictions that make additional features appear necessary.
Lisp originally stood for LISt Processor. In Scheme/Lisp a program is a series of terms, where terms are atoms or lists of terms. The pseudocode for evaluating Scheme expressions is unbelievably simple:
Here's all the code from the first page, translated to Scheme:
(define a 15) (define d '(1 2)) (let* ((a 20)) (+ (car d) a)) (define (f x y) (let* ((nested (lambda (z) (+ a x z)))) (nested y)))
Scheme does not statically infer types. The only operations that cause a type error are those that apply a function to values of incorrect type, e.g. adding a string to a number.
In part due to its dynamic type system, Scheme is extraordinarily flexible. For example, you can model an object-oriented "object" as higher-order function:
(define (make-student name id-num) (lambda (message) (cond ((equal? message 'getName) name) ((equal? message 'getID) id-num) ((equal? message 'cloneMe) (make-student (name id-num))) (else "INVALID MESSAGE")))) (define alice (make-student "Alice" 123456)) (display (alice 'getName))
The single quote means "do not evaluate". As used above, it signifies that getName etc. are literal symbols, not names to be evaluated.
Things to think about on your own: Why can't you do this in ML? How might you get around this problem? (Hint: think of your ML interpreter, which internally had to model dynamic typing.)