4

I am reading SICP to learn Lisp, the book uses Scheme dialect. I want accommodate it to emacs lisp, I find something weird in 3.1.1 and cannot figure it out, say I have a test.el file:

(defun make-account ()
  (defun withdraw (amount)
    (print "in withdraw"))
  (lambda () 'withdraw))


(setq a (make-account))
(withdraw 10)

Execute this file with command emacs -batch -l test.el yields "in withdraw". Why is not function withdraw local in make-account?

And if I comment out (setq a (make-account)), it complains about Symbol’s function definition is void: withdraw.

What am I missing here? Thanks for any help!

  • withdraw is global, but only declared when make-account has been run. – choroba Dec 24 '18 at 3:36
  • so how can I define a local function just like define in Scheme? then as a returned value which can be called outside? – Alaneuler Dec 24 '18 at 3:48
  • 2
    Emacs Lisp is not Scheme. They're completely different dialects of Lisp, with different idioms. What you'd solve in Scheme by defining a local function is solved in Emacs Lisp by defining another top-level function and using it. For this reason it's common to see functions following the my--helper-function or my-helper-function-1 pattern. – wasamasa Dec 24 '18 at 10:31
3

Use cl-labels to define local functions (cl-flet works, but recursive function reports error), and in the beginning of the .el file enable the lexical scoping:

;; -*- lexical-binding: t -*-
(defun make-account ()
  (cl-labels ((withdraw (amount)
                        (print "in withdraw")))
    (lambda () #'withdraw)))

Remember the # before the quoting of function name (for why # is needed, see link).

I've tested this in ielm mode, the output of (make-account) is:

ELISP> (make-account)
(closure
 ((--cl-withdraw-- closure #1
                   (amount)
                   (print "in withdraw"))
  t)
 nil --cl-withdraw--)

We can see that the closure for withdraw is returned.

Thanks to @stefan and @db48x for helpful information.

0

You probably want to use flet:

(defun make-account ()
  (flet ((withdraw (amount)
                   (print "in withdraw")))
    (lambda () 'withdraw)))
  • 2
    cl.el's flet doesn't behave like Common-Lisp's so this won't work. But if you change that to use cl-flet (and then fix the code to use #'withdraw and to add a pair of parentheses around withdraw's definition), and tell Aleneuler to use lexical-binding then indeed it should work. – Stefan Dec 24 '18 at 5:23
  • @stefan or db48x: Please consider adding such info to the answer (or another answer). Comments can be deleted at any time. Thx. – Drew Dec 24 '18 at 6:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.