From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: "Pascal J. Bourguignon" Newsgroups: gmane.emacs.help Subject: Re: never use `eval' Date: Fri, 17 Jul 2015 03:39:41 +0200 Organization: Informatimago Message-ID: <87d1zrlfz6.fsf@kuiper.lan.informatimago.com> References: <87io9lmb4z.fsf@mbork.pl> <87oajdkqc7.fsf@kuiper.lan.informatimago.com> <87zj2w29ch.fsf@nl106-137-147.student.uu.se> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Trace: ger.gmane.org 1437097220 8384 80.91.229.3 (17 Jul 2015 01:40:20 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Fri, 17 Jul 2015 01:40:20 +0000 (UTC) To: help-gnu-emacs@gnu.org Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Fri Jul 17 03:40:19 2015 Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1ZFudP-0004sL-51 for geh-help-gnu-emacs@m.gmane.org; Fri, 17 Jul 2015 03:40:19 +0200 Original-Received: from localhost ([::1]:42517 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZFudO-0007xX-2U for geh-help-gnu-emacs@m.gmane.org; Thu, 16 Jul 2015 21:40:18 -0400 Original-Path: usenet.stanford.edu!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail Original-Newsgroups: gnu.emacs.help Original-Lines: 321 Original-X-Trace: individual.net Sr/GWQtRqN/wH4SL351teg3X4BmuMPFY5k269L29NXJ0kVJyRz Cancel-Lock: sha1:NzFhODk3YmQyMzdmMDc3ZTYwOTcxNjc0NzhmMmNkYWNiZjU0NmRhMg== sha1:Oe3HtcjJ2h5v3mzcNPPVFR7N5es= Face: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAQMAAABtzGvEAAAABlBMVEUAAAD///+l2Z/dAAAA oElEQVR4nK3OsRHCMAwF0O8YQufUNIQRGIAja9CxSA55AxZgFO4coMgYrEDDQZWPIlNAjwq9 033pbOBPtbXuB6PKNBn5gZkhGa86Z4x2wE67O+06WxGD/HCOGR0deY3f9Ijwwt7rNGNf6Oac l/GuZTF1wFGKiYYHKSFAkjIo1b6sCYS1sVmFhhhahKQssRjRT90ITWUk6vvK3RsPGs+M1RuR mV+hO/VvFAAAAABJRU5ErkJggg== X-Accept-Language: fr, es, en User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux) Original-Xref: usenet.stanford.edu gnu.emacs.help:213482 X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Original-Sender: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.help:105768 Archived-At: Emanuel Berg writes: > Remember when I said the byte compiler should be more > offensive and offer more elaborate style warnings? > > And this is a good example of that! > > Even in the Emacs help > > (describe-function 'eval) > > it doesn't say anything about the dangers of the > "nil environment": > (eval FORM &optional LEXICAL) > Evaluate FORM and return its value. > If LEXICAL is t, evaluate using lexical scoping. > LEXICAL can also be an actual lexical environment, > in the form of an alist mapping symbols to > their value. Doesn't seem to work: (setf lexical-binding t) (let ((x 42)) (eval 'x t)) Debugger entered--Lisp error: (void-variable x) (let ((x 42)) (eval 'x nil)) Debugger entered--Lisp error: (void-variable x) > This book - > > @book{artificial-intelligence-and-the-design, > title = {Artificial Intelligence and the Design of Expert Systems}, > author = {George Luger and William Stubblefield}, > publisher = {Benjamin-Cummings}, > year = 1989, > ISBN = 0805301399 > } > > - has a chapter on LISP. One of the first things > they mention is `eval', and they mention it in > connection to the `quote', eval "undoing" quote so you > get the feeling it is as indispensible. There are > several examples using it. Without lexical binding, there's no other environment than the global environment, so it works better: (setf lexical-binding nil) (let ((x 42)) (eval 'x)) --> 42 Old lisp didn't have lexical binding (the famous funarg problem), and this is why eval could have been more useful then. But once lexical bindings were introduced in scheme and in Common Lisp, and now in emacs lisp, you cannot be so lenient with eval. What this means for the modern (emacs or Common) lisper? That you should take old lisp code that uses eval with some precautions, such as, using (setf lexical-binding nil) or making sure that all the variables are declared special. See for example, the definition of the DEFINE macro in: http://www.informatimago.com/develop/lisp/com/informatimago/small-cl-pgms/wang.html which declares all the variables with DEFPARAMETER, hence declaiming them to be special and of the dynamic binding persuasion. > Their other LISP isn't that good either by the way. > There are several examples where they do (car l) > several times in one execution branch instead of > `let'ing it be a local identifier; It should not be a problem, at that time, or nowadays. In the old days, car and cdr were quite the optimized instructions: they corresponded to 7090 machine instructions, or to lisp machine instructions, so calling (car x) several times was probably faster than storing the result in a variable or even a register (because of the possible register spilling) and calling it up again. And nowadays, with honest compilers performing some common subexpression elimination optimization, it should compile to exactly the same. In the case of ccl, the code is quite similar, but indeed (car x) (car x) will hit the (cache) memory each time, recomputing the offset (assumedly in the pipeline so at zero cost) each time, while using a temporary variable saves it in a register. cl-user> (disassemble (compile nil (lambda (x) (list (car x) (car x) (car x))))) L0 (leaq (@ (:^ L0) (% rip)) (% fn)) ; [0] (pushq (% rbp)) ; [7] (movq (% rsp) (% rbp)) ; [8] (pushq (% save0)) ; [11] (movq (% arg_z) (% save0)) ; [13] (pushq (@ 5 (% save0))) ; [16] (pushq (@ 5 (% save0))) ; [20] (pushq (@ 5 (% save0))) ; [24] (movl ($ 24) (% nargs)) ; [28] (leaq (@ (:^ L53) (% fn)) (% temp2)) ; [33] (nop) ; [40] (nop) ; [43] (jmpq (@ .SPCONSLIST)) ; [46] L53 (leaq (@ (:^ L0) (% rip)) (% fn)) ; [53] (popq (% save0)) ; [60] (leaveq) ; [62] (retq) ; [63] nil cl-user> (disassemble (compile nil (lambda (x) (let ((y (car x))) (list y y y))))) L0 (leaq (@ (:^ L0) (% rip)) (% fn)) ; [0] (pushq (% rbp)) ; [7] (movq (% rsp) (% rbp)) ; [8] (pushq (% arg_z)) ; [11] (pushq (% save0)) ; [12] (movq (@ 5 (% arg_z)) (% save0)) ; [14] (pushq (% save0)) ; [18] (pushq (% save0)) ; [20] (pushq (% save0)) ; [22] (movl ($ 24) (% nargs)) ; [24] (leaq (@ (:^ L45) (% fn)) (% temp2)) ; [29] (nop) ; [36] (jmpq (@ .SPCONSLIST)) ; [38] L45 (leaq (@ (:^ L0) (% rip)) (% fn)) ; [45] (popq (% save0)) ; [52] (leaveq) ; [54] (retq) ; [55] nil cl-user> (optimization) (optimize (safety 0) (debug 0) (speed 3) (space 3) (compilation-speed 1)) But in the case of sbcl, it compiles to the exact same instructions (the only difference being in the procedure entry, probably allocating some space on the stack that is left unused in the let case). diff -c /tmp/b /tmp/a *** /tmp/b Fri Jul 17 03:29:50 2015 --- /tmp/a Fri Jul 17 03:29:56 2015 *************** *** 1,10 **** ! * (disassemble (compile nil (lambda (x) (list (car x) (car x) (car x))))) ; disassembly for (LAMBDA (X)) 488B4AF9 MOV RCX, [RDX-7] ; no-arg-parsing entry point - 488B7AF9 MOV RDI, [RDX-7] - 488B72F9 MOV RSI, [RDX-7] 488BD1 MOV RDX, RCX 49896C2440 MOV [R12+64], RBP 4D8B5C2418 MOV R11, [R12+24] 498D5B30 LEA RBX, [R11+48] --- 1,10 ---- ! * (disassemble (compile nil (lambda (x) (let ((y (car x))) (list y y y))))) ; disassembly for (LAMBDA (X)) 488B4AF9 MOV RCX, [RDX-7] ; no-arg-parsing entry point 488BD1 MOV RDX, RCX + 488BF9 MOV RDI, RCX + 488BF1 MOV RSI, RCX 49896C2440 MOV [R12+64], RBP 4D8B5C2418 MOV R11, [R12+24] 498D5B30 LEA RBX, [R11+48] *************** *** 36,38 **** --- 36,39 ---- 488D5B07 LEA RBX, [RBX+7] EBB3 JMP L0 NIL + * Diff finished. Fri Jul 17 03:30:00 2015 * (disassemble (compile nil (lambda (x) (list (car x) (car x) (car x))))) ; disassembly for (LAMBDA (X)) ; 02AB631F: 488B4AF9 MOV RCX, [RDX-7] ; no-arg-parsing entry point ; 23: 488B7AF9 MOV RDI, [RDX-7] ; 27: 488B72F9 MOV RSI, [RDX-7] ; 2B: 488BD1 MOV RDX, RCX ; 2E: 49896C2440 MOV [R12+64], RBP ; 33: 4D8B5C2418 MOV R11, [R12+24] ; 38: 498D5B30 LEA RBX, [R11+48] ; 3C: 49395C2420 CMP [R12+32], RBX ; 41: 7642 JBE L2 ; 43: 49895C2418 MOV [R12+24], RBX ; 48: 498D5B07 LEA RBX, [R11+7] ; 4C: L0: 488BC3 MOV RAX, RBX ; 4F: 488950F9 MOV [RAX-7], RDX ; 53: 4883C010 ADD RAX, 16 ; 57: 488940F1 MOV [RAX-15], RAX ; 5B: 488978F9 MOV [RAX-7], RDI ; 5F: 4883C010 ADD RAX, 16 ; 63: 488940F1 MOV [RAX-15], RAX ; 67: 488970F9 MOV [RAX-7], RSI ; 6B: 48C7400117001020 MOV QWORD PTR [RAX+1], 537919511 ; 73: 49316C2440 XOR [R12+64], RBP ; 78: 7402 JEQ L1 ; 7A: CC09 BREAK 9 ; pending interrupt trap ; 7C: L1: 488BD3 MOV RDX, RBX ; 7F: 488BE5 MOV RSP, RBP ; 82: F8 CLC ; 83: 5D POP RBP ; 84: C3 RET ; 85: L2: 6A30 PUSH 48 ; 87: 4C8D1C2570724200 LEA R11, [#x427270] ; alloc_tramp ; 8F: 41FFD3 CALL R11 ; 92: 5B POP RBX ; 93: 488D5B07 LEA RBX, [RBX+7] ; 97: EBB3 JMP L0 NIL * (disassemble (compile nil (lambda (x) (let ((y (car x))) (list y y y))))) ; disassembly for (LAMBDA (X)) ; 02B12EAF: 488B4AF9 MOV RCX, [RDX-7] ; no-arg-parsing entry point ; EB3: 488BD1 MOV RDX, RCX ; EB6: 488BF9 MOV RDI, RCX ; EB9: 488BF1 MOV RSI, RCX ; EBC: 49896C2440 MOV [R12+64], RBP ; EC1: 4D8B5C2418 MOV R11, [R12+24] ; EC6: 498D5B30 LEA RBX, [R11+48] ; ECA: 49395C2420 CMP [R12+32], RBX ; ECF: 7642 JBE L2 ; ED1: 49895C2418 MOV [R12+24], RBX ; ED6: 498D5B07 LEA RBX, [R11+7] ; EDA: L0: 488BC3 MOV RAX, RBX ; EDD: 488950F9 MOV [RAX-7], RDX ; EE1: 4883C010 ADD RAX, 16 ; EE5: 488940F1 MOV [RAX-15], RAX ; EE9: 488978F9 MOV [RAX-7], RDI ; EED: 4883C010 ADD RAX, 16 ; EF1: 488940F1 MOV [RAX-15], RAX ; EF5: 488970F9 MOV [RAX-7], RSI ; EF9: 48C7400117001020 MOV QWORD PTR [RAX+1], 537919511 ; F01: 49316C2440 XOR [R12+64], RBP ; F06: 7402 JEQ L1 ; F08: CC09 BREAK 9 ; pending interrupt trap ; F0A: L1: 488BD3 MOV RDX, RBX ; F0D: 488BE5 MOV RSP, RBP ; F10: F8 CLC ; F11: 5D POP RBP ; F12: C3 RET ; F13: L2: 6A30 PUSH 48 ; F15: 4C8D1C2570724200 LEA R11, [#x427270] ; alloc_tramp ; F1D: 41FFD3 CALL R11 ; F20: 5B POP RBX ; F21: 488D5B07 LEA RBX, [RBX+7] ; F25: EBB3 JMP L0 NIL * In the case of emacs, being an implementation targetting a lisp machine VM, the multiple (cdr x) version looks definitely more optimized (less VM instructions), as expected. (disassemble (byte-compile (lambda (x) (let ((y (car x))) (list y y y))))) byte code: args: (x) 0 varref x 1 car 2 dup 3 varbind y 4 dup 5 varref y 6 list3 7 unbind 1 8 return (disassemble (byte-compile (lambda (x) (list (car x) (car x) (car x))))) byte code: args: (x) 0 varref x 1 car 2 varref x 3 car 4 varref x 5 car 6 list3 7 return > and, they speak > appreciatively of all the different types of recursion > (tail etc.) but at least so far hasn't mentioned (and > will they?) anything of the drawbacks of recursion in > terms of efficiency. (When I started out with Lisp, > coming "closest" from SML of the languages I did > then, I did recursion all the time by the way.) Again, if you have tail call optimization, then there's no recursion occuring, and efficiency is equivalent to iteration (because in effect, the compiler generates an iteration). Of course, CL implementations and emacs lisp don't necessarily implement TCO, so recursion could be less efficient. But as long as response times are below the 300 ms threshold, it's ok. ;-) -- __Pascal Bourguignon__ http://www.informatimago.com/ “The factory of the future will have only two employees, a man and a dog. The man will be there to feed the dog. The dog will be there to keep the man from touching the equipment.” -- Carl Bass CEO Autodesk