✍ Revenge of the nerds

Comentários ao artigo de Paul Graham sobre implementação da função acumulador

Em 2002 Paul Graham escreveu um artigo intitulado "Revenge of the Nerds" que, entre outras coisas, fala das vantagens do uso de Lisp e o que o torna diferente das outras linguagens de programação. O que me levou até este artigo foi aquilo de que se trata no apêndice, i.e., sobre o poder de uma linguagem de programação.

As an illustration of what I mean about the relative power of programming languages, consider the following problem. We want to write a function that generates accumulators— a function that takes a number n, and returns a function that takes another number i and returns n incremented by i. (That's incremented by, not plus. An accumulator has to accumulate.)

Em Common Lisp a função que faz isto é dada por

(defun foo (n)
  (lambda (i)
    (incf n i)))
Para ver como funciona põe-se
> (setq a (foo 3))
#<FUNCTION :LAMBDA (I) (INCF N I)>

> (funcall a 1)
4

Fui ver então se a mesma função foo, definida anteriormente, funciona em Emacs Lisp. Depois de (funcall a 1) o resultado é este:

  Debugger entered--Lisp error: (void-variable n)
  (+ n i)
  (setq n (+ n i))
  (incf n i)
  (lambda (i) (incf n i))(1)
  funcall((lambda (i) (incf n i)) 1)
  eval((funcall a 1))
  eval-last-sexp-1(nil)
  eval-last-sexp(nil)
  call-interactively(eval-last-sexp

Aparentemente a variável n é considerada como local na definição de foo, mas isto não faz sentido nenhum. É um bug acidental ou propositado do dialecto de Lisp que corre no Emacs?

Email enviado para a mailing list do Emacs. Novidades aqui, mais tarde.

Já recebi a resposta da mailing list. Aqui está ela:

Should not the function foo in Common Lisp work with Emacs Lisp?

(defun foo (n)
  (lambda (i)
    (incf n i)))

No, that won't work because emacs has no lexical scoping and that code is a closure. See the elisp manual:

,----[ (info "(elisp)Extent") ]
|    To illustrate this, the function below, `make-add', returns a
| function that purports to add N to its own argument M.  This would work
| in Common Lisp, but it does not do the job in Emacs Lisp, because after
| the call to `make-add' exits, the variable `n' is no longer bound to
| the actual argument 2.
|
|      (defun make-add (n)
|          (function (lambda (m) (+ n m))))  ; Return a function.
|           => make-add
|      (fset 'add2 (make-add 2))  ; Define function `add2'
|                                 ;   with `(make-add 2)'.
|           => (lambda (m) (+ n m))
|      (add2 4)                   ; Try to add 2 to 4.
|      error--> Symbol's value as variable is void: n
|
|    Some Lisp dialects have "closures," objects that are like functions
| but record additional variable bindings.  Emacs Lisp does not have
| closures.
`----

Mais uma resposta da mailing list:

Emacs Lisp has dynamic scoping, not lexical scoping. If you want to emulate lexical scoping, (require 'cl-macs) and use lexical-let:

(defun foo (n)
  (lexical-let ((lexn n))
    (lambda (i)
      (incf lexn n))))
Palavras chave/keywords: Lisp, Emacs, Paul Graham, bug

Última actualização/Last updated: 2014-11-13 [00:04]


1999-2014 (ç) Tiago Charters de Azevedo

São permitidas cópias textuais parciais/integrais em qualquer meio com/sem alterações desde que se mantenha este aviso.

Verbatim copying and redistribution of this entire page are permitted provided this notice is preserved.