Composing Fexprs Preserves Short-circuit Evaluation.

; One of the well known advantages of special operators over
; functions is that special operators apply "short-cuirciting".
; For example, operator "and" that evaluates
;  (and (= 1 1) (= 2 2) (= 3 4) (= 5 (slow-function 6)))
; will never evaluate clause (= 5 (slow-function 6)). Instead,
; after (= 3 4) is recognized to be false, there is no theoretical
; possibility that whole expression evaluates to true, and
; nil is returned without evaluating other clauses.
; This is significant difference between operators and functions.
; If "and" was defined as function, all clauses would be evaluated
; first, and then passed to the operator "and."
; Only today I came to idea to test whether operator compose I
; defined few days ago, which can be applied on fexprs as well as
; on functions, preserves short-circuiting.
; I'll test it by defining two well known logical operators,
; nor and nand.

(set 'nor (compose not or))
(set 'nand (compose not and))

; Let us test whether 'nor' and 'nand' preserve short-circuit
; evaluation.

(nand (println "this should be done ")
      (println "and this should be done")
      (println (setf j nil))
      (println "but this shouldn't be done"))

; this should be done
; and this should be done
; nil

(println "---")

(nor (println (= 1 2))
     (println (= 1 3))
     (println "and something different")
     (println "this shouldn't be done"))
; ---
; nil
; nil
; and something different

; Everything works. Beautiful, isn't it?


Bad weather in Croatia.

Composition of Functions or Macros.

; The composition of the functions is one of the basic mathematical
; operations. In this post, I'll try to define composition of
; functions and macros (Newlisp macros=fexprs) in Newlisp.
; Such composition should satisfy the following:
;     ((composition 'f1 ... 'fn) _ _ _) = (f1 (f2 ... (fn _ _ _)))
; for all functions and macros.
; It wasn't that easy as I thought. First, I limited myself on the
; case of the composition of two functions. After some experimentation
; I came to that:

(set 'compose2 (lambda(f g)
                 (expand (lambda-macro()(letex((L (cons 'g (args))))(f L)))
                          'f 'g)))

; I tested it on simple example:

(println (compose2 'sin 'cos))

; (lambda-macro ()
;  (letex ((L (cons 'cos (args)))) (sin L)))

(println ((compose2 'sin 'cos) 3) (sin (cos 3))) ; OK, it works.

; Then I tested it on two special, well, entities, i.e. identity
; function and identity macro:

(set 'I (lambda(x)x))
(set 'IM (lambda-macro(x)x))

(println ((compose2 'I 'sin) 3)) ; 0.1411200081, as it should be, i.e. (I (sin 3))
(println ((compose2 'sin 'I) 3)) ; 0.1411200081, as it should be, i.e. (sin (I 3))
(println ((compose2 'IM 'sin) 3)) ; (sin 3), as it should be, i.e. (IM (sin 3))
(println ((compose2 'sin 'IM) 3)) ; 0.1411200081, as it should be (sin (IM 3))

; OK; it appears it worked. Now I'll have to solve multi-version,
; I.e. composition of many functions or macros

(set 'compose (lambda()
                 (case (length (args))
                    (0 I)               ; because (I (S x)) <=> (S x),
                                        ; no matter if S is function or macro
                                        ; so I can be always added from the left.
                    (1 (first (args)))
                    (2 (apply compose2 (args)))
                    (true (compose2 (first (args)) (apply compose (rest (args))))))))

(println ((compose sqrt) 65536)) ; 256
(println ((compose sqrt sqrt) 65536)) ; 16
(println ((compose sqrt sqrt sqrt) 65536)) ; 4

; OK, it works as well. However, result of the composing is
; rather complicated because of recursive definition

(println (compose 'f1 'f2 'f3 'f4))

; (lambda-macro ()
;   (letex ((L (cons '(lambda-macro ()(letex ((L (cons '(lambda-macro ()(letex ((L (cons 'f4 (args))))
;                                                                              (f3 L)))
;                                                      (args))))
;                                             (f2 L)))
;                    (args))))
;         (f1 L)))
; If iteration is used for definition of compose, then composition
; can be both shorter and faster - but not more elegant.


Alan Kay on Lisp and Fexprs.


Alan Kay on Lisp and Fexprs

Alan Kay is designer of Smalltalk. This citation is known, but not as well as it should be, and I think it deserves attention separately of many posts I wrote on fexprs.

"I could hardly believe how beautiful and wonderful the idea of LISP was. I say it this way because LISP had not only been around enough to get some honest barnacles, but worse, there were deep flaws in its logical foundations. By this, I mean that the pure language was supposed to be based on functions, but its most important components -- such as lambda expressions, quotes, and conds -- were not functions at all, and instead were called special forms. Landin and others had been able to get quotes and cons in terms of lambda by tricks that were variously clever and useful, but the flaw remained in the jewel. In the practical language things were better. There were not just EXPRs (which evaluated their arguments), but FEXPRs (which did not). My next questions was, why on Earth call it a functional language? Why not just base everything on FEXPRs and force evaluation on the receiving side when needed?

I could never get a good answer, but the question was very helpful when it came time to invent Smalltalk, because this started a line of thought that said 'take the hardest and most profound thing you need to do, make it great, an then build every easier thing out of it.'"

Alan Kay,
The Early History of Smalltalk.,
in: Bergin, Jr., T.J., and R.G. Gibson.
History of Programming Languages - II,
ACM Press, New York NY, and
Addison-Wesley Publ. Co., Reading MA 1996,
pp. 511-578

If you liked this article, you might be interested in On Pitman's 'Special forms in Lisp'.