The Speed of Clojure's Eval.



I've seen discussion about eval performance on Clojure mailing list and decided to test the speed of Clojure's eval, additionally to other Lisp dialects I tested previously. It turned that Clojure has slower eval than other Lisp implementations. Althought Clojure has two features that slow down eval - macros like most of Lisp dialects and some of the built-in primitives are macros as well, as in CL, I guess that the main reason for slow eval in that particular test is in integration with Java.

All results and the code are integrated in my older post:


 Speed of Eval in Some Lisp Implementations. 



---

Symbols as Sexprs and Hygienic Fexprs.











; During last year of use of Newlisp I changed my opinion about
; one important thing: encoding information in symbols.

; Initially, I thought it is mistake, or attempt for escape from
; dry, but essential Lisp syntax, known in most of Lisp languages.
; See, for example, apostrophe - or even worse, "loop" in some
; dialects. I criticized my Newlisp coleague Cyril Slobin
; who did it once.

; However, soon I concluded that there is no good alternative for
; encoding of information in symbols. For example, in one of the
; first posts here, I tried to define operators similar to +=,
; -=, *= etc in C. If you didn't used these operators, x+=1 is
; same thing as x=x+1; x*=2 is same as x=x*2.
;
; What does it mean, encoding information in symbols? Take a look
; on C operators
;
;                          +=, -=, *=,
;
;
; again. The names of these operators are not *just names*, but also
; descriptions of the operators. The programmer who learns C probably
; mentally parse these names while programming for some time, until he
; automatizes use of the operators.
;
; If these operators are defined in Newlisp program, then the
; names must be defined by program as well. And what should be the
; names of the operators defined with
;
;          (operator1 x y) <=> (setq x (+ x y))
;          (operator2 x y) <=> (setq x (- x y))
;          ...?
;
; From my point of view, natural choice of the names could be
; setq+ and setq-.
;
; Another example of generated symbol names I used is in post
; in which I defined functions for prevention of accidental name
; clashes. If function f is, for example, defined with
;
;                  (set 'f (lambda(x)(x y)))
;
; then (protect1 'f '(x y)) replaced definition of f with
;
;                    (lambda(f.x)(f.x f.y))
;
; and later, I replaced it with
;
;                  (lambda([f.x])([f.x] [f.y]))
;
; How bracket get into combination? Because, I have find that
; I might need to distinguish names like
;
;                    f.[x.1] and [f.x].1
;
; At this point, my variable names started to look increasingly
; like s-expression, so I assumed that it might be useful to
; assume that our usual one-word symbols as just special cases
; of symbols in the form of s-expressions.
;
; Newlisp allow us to use "ilegal" symbol names, so theoretically,
; I can use symbols of the form (f x) - with blanks and parenthesis
; as characters - but such symbols cannot be printed out and readed
; again. So, symbols - sexprs must use different forms of parentheses
; and delimiters.

; Unfortunately, dot is not good choice for delimiter, because dot
; can be part of the number, so [f.1.3] is ambigious - is it (f 1.3),
; or (f 1 3)?
;
; Also, square and curly brackets have special meanings in Newlisp
; syntax, so more exotic choices are necessary.

; In following part of the code, I'll show how I defined symbols in
; the form of s-expression and how these are used to redefine functions
; protect1 (fast - and protecting from practically all kinds of accidental
; variable clashes) and protect2 (slow - protecting from accidental
; variable name clashes in some rare, in Newlisp practice unknown.
; but still possible cases.)
; Details are described in my previous posts in series
;
; "Don't fear dynamic scope."
;
; "Don't fear dynamic scope (2)."


;---------------------------------------------------------------
; First, we'll define equivalents in one, centralized place

(set 'left-parenthesis-equivalent ".<.")
(set 'right-parenthesis-equivalent ".>.")
(set 'blank-equivalent "___")
(set 'apostrophe-equivalent "`")
(set 'quotation-mark-equivalent "~")

;---------------------------------------------------------------
; Next, we'll define two pairs of functions for conversion from
; s-expression to string and vice versa.

(set 'symbol-to-sexpr
     (lambda(S)
       (setq S (string S))
       (setq S (replace left-parenthesis-equivalent S "("))
       (setq S (replace right-parenthesis-equivalent S ")"))
       (setq S (replace blank-equivalent S " "))
       (setq S (replace apostrophe-equivalent S "'"))
       (setq S (replace quotation-mark-equivalent "\""))
       (eval-string (append "'" S))))

(set 'sexpr-from-symbol symbol-to-sexpr)

(set 'sexpr-to-symbol
     (lambda(L)
       (setq L (string L))
       (setq L (replace "(" L left-parenthesis-equivalent))
       (setq L (replace ")" L right-parenthesis-equivalent))
       (setq L (replace " " L blank-equivalent))
       (setq L (replace "'" L apostrophe-equivalent ))
       (setq L (replace "\"" L quotation-mark-equivalent ))
       (sym L)))
       
(set 'symbol-from-sexpr sexpr-to-symbol)

; Let us see on example, how these functions work:

(println (symbol-from-sexpr '(* (+ x y) (- x y))))

;
;           .<.*___.<.+___x___y.>.___.<.-___x___y.>..>.
;
; Pretty much like normal s-expression, while < and > pretend to be
; parenthesis, and exotic dots pretend to be blank characters.
;
;---------------------------------------------------------------
; Now, I'll define "protected version" of function set:
;
;   * (set-protected1 function/macro-name original-code variables)


(set 'set-protected1
  (lambda(function/macro-name definition-code variables)
    (set function/macro-name
      (eval (list 'letex
                  (map (lambda(x)
                         (list x
                               (list 'quote
                                     (symbol-from-sexpr
                                        (list function/macro-name
                                              x)))))
                        variables)
                  definition-code)))))


;---------------------------------------------------------------
; Example:

(set-protected1 'f (lambda(x y z)(x y z)) '(x z))

(println f) ;=> (lambda (.<.f___x.>. y .<.f___z.>.)
            ;              (.<.f___x.>. y .<.f___z.>.))


;---------------------------------------------------------------
; Similarly, the version protect1 that protects the functions
; that already exist:

(set 'protect1 (lambda(function/macro-name variables)
                  (set-protected1 function/macro-name
                                (eval function/macro-name)
                                variables)))


;---------------------------------------------------------------
; In next step, I'll use set-protected1 and protect1 to define -
; protected versions of set-protected1 and protect1.

((copy set-protected1) 'set-protected1
                        set-protected1
                        '(function/macro-name definition-code
                                              variables
                                              x))
                        
((copy protect1) 'protect1 '(function/macro-name variables))


;---------------------------------------------------------------
; In following step, I'll define set-protect2 and protect2. These
; two functions should be able to protect functions even from the
; most demanding funarg problems. Details are explained in "Don't
; fear dynamic scope." articles, links are above.  
    
(set 'set-protected2
  (lambda(function/macro-name definition-code variables)
    (set function/macro-name
      (expand (lambda-macro()
                (let((name-and-counter
                        (symbol-from-sexpr (list 'function/macro-name
                                                 (inc counter)))))
                    
                    (set-protected1 name-and-counter
                                    definition-code
                                    'variables)
                    
                    (first (list (eval (cons name-and-counter
                                             $args))    
                                 (dolist(i 'variables)
                                    (delete (symbol-from-sexpr
                                              (list name-and-counter
                                                    i))))
                                 (delete name-and-counter)))))
                   
               'function/macro-name 'definition-code 'variables))))
               
; set-protected2 is the most important function in this post.
; In this version it is more than twice shorter and, I hope,
; simpler than in last version.


;---------------------------------------------------------------
; Example of set-protected2

(set-protected2 'f (lambda(x y z)(x y z)) '(x z))
(println f)

; (lambda-macro ()
;  (let ((name-and-counter (symbol-from-sexpr (list 'f (inc counter)))))
;   (set-protected1 name-and-counter (lambda (x y z) (x y z)) '(x z))
;   (first (list (apply name-and-counter $args)
;     (dolist (i '(x z))
;      (delete (symbol-from-sexpr (list name-and-counter i))))
;     (delete name-and-counter)))))


;---------------------------------------------------------------
; Similarly, the version protect2 that protects the functions
; and macros that already exist:
              
(set 'protect2
     (lambda(function/macro-name variables)
        (set-protected2 function/macro-name
                        (eval function/macro-name)
                        variables)))


;---------------------------------------------------------------
; In next step, I'll use protect1 to define -
; protected versions of set-protected2 and protect2.

(protect1 'set-protected2 '(function/macro-name definition-code
                            variables counter name-and-counter i))
                            
(protect1 'protect2 '(function/macro-name variables))


;---------------------------------------------------------------
; Finally, hard example of funarg problem: recursive fexpr hard-example
; call itself, passing the function encapsulating one free variable
; from one instance of the hard-example to another. We'll see whether
; protect2 will protect it.
;
; If (hard-example (lambda(x)x)) returns 1 2 3 1 2 3 then free variable
; is overshadowed with same variable in other instance.
;
; If (hard-example (lambda(x)x)) returns 1 1 1 1 2 3 then it is not
; overshadowed.

(define-macro (hard-example f)
      (for(i 1 3)
         (unless done          ; avoiding infinite
             (set 'done true)  ; recursion
             (hard-example (lambda(x)i)))  
                               ; One recursive call with function
                               ; (lambda(x)i)

         (println i " =>" (f i)))) ; which i will be printed?
                                   ; i=1 2 3 means inner is overriden


;---------------------------------------------------------------
; First, without protection:

(set 'done nil)
(hard-example (lambda(x)x))
 
; 1 =>1
; 2 =>2
; 3 =>3
; 1 =>1
; 2 =>2
; 3 =>3

; Overshadowing happened.

;---------------------------------------------------------------
; Then, after protection:

(set 'done nil)
(protect2 'hard-example '(f i x))
(hard-example (lambda(x)x))

; 1 =>1
; 2 =>1
; 3 =>1
; 1 =>1
; 2 =>2
; 3 =>3

; Overshadowing prevented.

;---------------------------------------------------------------
; Finally, we'll take a look whether all variables intended to
; be temporary, like .<.hard-example___1.>. etc. are deleted.

(dolist(i (symbols))
  (if (starts-with (string i) left-parenthesis-equivalent)
      (println i)))
      
; .<.*___.<.+___x___y.>.___.<.-___x___y.>..>.
; .<.f___x.>.
; .<.f___z.>.
; .<.protect1___function/macro-name.>.
; .<.protect1___variables.>.
; .<.protect2___function/macro-name.>.
; .<.protect2___variables.>.
; .<.set-protected1___definition-code.>.
; .<.set-protected1___function/macro-name.>.
; .<.set-protected1___variables.>.
; .<.set-protected1___x.>.
; .<.set-protected2___counter.>.
; .<.set-protected2___definition-code.>.
; .<.set-protected2___function/macro-name.>.
; .<.set-protected2___i.>.
; .<.set-protected2___name-and-counter.>.
; .<.set-protected2___variables.>.
;
; Yes, they are deleted.
;
; Another test, this one taken from recent John Shutt's disertation
; on Kernel programming language.

(define y 3)
(set 'f (lambda (x) (+ x y)))
(set 'g (lambda (y) (+ 1 (f y))))
(println (g 5))                     ;=> 11

; This code in lexically scoped Lisp evaluates to 9. In dynamically
; scoped Lisp it evaluates to 11. If you are surprised with 11,
; that means you didn't recognized that y from definition of g will
; overshadow top level y during evaluation of (f y). But - it will.
; If you want to use different objects, you have to use different
; names; and, if it is boring to invent new names for bounded variables,
; set-protected (both versions) will do that for you:

(define y 3)
(set-protected2 'f (lambda (x) (+ x y)) '(x))
(set-protected2 'g (lambda (y) (+ 1 (f y))) '(y))
(println (g 5))     ;=> 9

(exit)

;
; In further posts, I'll explore that idea of symbols as sexprs
; deeper.



---