The following was written in 2003. More recently I have noticed that Scheme’s lack of synergy keeps it from bringing the abstracted parts of two objects of the same ‘class’ into one scope where they can be operated upon. This is so easy in C++ as to escape attention.
I had lunch today with Chip, Markm, Marcs & Chris. We discussed the language E and its lack of direct support of the class construct so fundamental to C++ and Java. I am not E savvy and am not a strong user of any of any such language, except perhaps Scheme.
There came into focus for me at least a tension between languages that take the lambda calculus as their inspiration, and those founded on a class construct. Scheme starts from lambda and Java from the class. Both inherit their biases from significant antecedents.
The beauty of the class is that there is a manifest body of code that is alone able to break the abstractions by which some functionality is implemented. It is so manifest that even a simple editor that balances braces and such can delimit the code.
Most people would deny that Scheme is an object language. Indeed Scheme seems to require some syntactic sugar to suit the needs of programmers that are used to the conveniences of typical object oriented languages. It is a strength, but also a great distraction that there are several incompatible ways of extending scheme to be object oriented. These have diverse advantages and it seems one must stick to one. This does not make it suitable for building large Scheme projects from different Scheme object dialects. C++ often fails here for lack of standard GC. Java does this tolerable well.
There has been a struggle on how to make E convenient or at least conventional to the programmer who has been brought up to understand the world as built from classes. (Recall Dijkstra’s observation that “conventional” does not imply “convenient”.) The pedagogical pattern of gradual revelation of language features was proposed. In the E case, syntactic sugar would make it appear that classes were directly supported and to some E programmers, it would be later revealed that the power delivered by classic Class discipline was instead delivered by explicit synergy.
The problem, I think, in any gradual revelation scheme, is whether code written before the revelation is sound. I think I have seen gradual revelation done so that the benefits are real and the deception is entirely harmless. I know that I have seen it attempted where the result is that programs have subtle bugs that can be explained only upon revelation. Said another way: “Are correctness proofs possible within the limited purview?”.
The truth is (I think) that code outside the static scope of
(define pair (lambda (f) (lambda (s) (lambda (b) ((b f) s))))) (define tru (lambda (t) (lambda (f) t))) (define fls (lambda (t) (lambda (f) f))) (define id (lambda (x) x)) (define test (lambda (l) (lambda(m) (lambda(n) ((l m) n))))) ; (((test tru) 4) 6) => 4 ; (((test fls) 4) 6) => 6 (define fst (lambda (p) (p tru))) (define snd (lambda (p) (p fls))) ; (let ((p34 ((pair 3) 4))) (list (fst p34) (snd p34))) => (3 4) (define zero (lambda (s) (lambda (z) z))) (define one (lambda (s) (lambda (z) (s z)))) (define two (lambda (s) (lambda (z) (s (s z))))) (define three(lambda (s) (lambda (z) (s (s (s z)))))) (define plus (lambda (m) (lambda (n) (lambda (s) (lambda (z) ((m s) ((n s) z))))))) (define times (lambda (m) (lambda (n) ((m (plus n)) zero)))) (define iszro (lambda (m) ((m (lambda (x) fls)) tru))) (define realnat (lambda (m) ((m (lambda (x) (+ x 1))) 0))) ; (realnat ((times three) two)) -> 6 (define fix (lambda (f) ((lambda (x) (f (lambda (y) ((x x) y)))) (lambda (x) (f (lambda (y) ((x x) y))))))) ; p 65 (define ff (lambda (ie) (lambda (x) (if (= 0 x) #t (if (= 0 (- x 1)) #f (ie (- (- x 1) 1))))))) (define iseven (fix ff)) let fix = (fun f -> ((fun x -> (f (fun y -> (x x) y))) (fun x -> (f (fun y -> (x x) y)))));; This expression has type 'a -> 'b but is here used with type 'aPierce (p 143) explains why you can’t do fix in strongly typed languages.