6

For example, when defining the natural number sequence stream, I can use

;; -*- lexical-binding: t; -*-

(defun nats (n)
  (cons n (lambda () (nats (1+ n)))))

(nats 0)
     => (0 closure ((n . 0) t) nil (nats (1+ n)))

But I want to call simply (nats) since the natural numbers begins with 0, and I don't want define a global helper function for nats since it is useless everywhere else. I tried the following, but it doesn't work.

;; -*- lexical-binding: t; -*-

(defalias 'nats
  (flet ((f (n) (cons n (lambda () (f (1+ n))))))
    (lambda () (f 0))))

(nats)
error--> nats: Symbol's function definition is void: f

It looks like f is not in the closure's environment. I also tried some other options. cl-letf produces the same result. cl-flet doesn't allow recursive local function definition.

  • 1
    If you want a recursive local function, you can use letrec (a newish Elisp construct) or cl-labels. – Stefan Sep 12 '16 at 19:47
8

Without cl-lib:

;; -*- lexical-binding: t; -*-

(defun nats ()
  (letrec ((inner (lambda (n) (cons n (lambda () (funcall inner (1+ n)))))))
    (funcall inner 0)))

(let* ((stream (nats))
       (i (car stream)))
  (while (< i 10)
    (message "Got %i" i)
    (setq stream (funcall (cdr stream))
          i (car stream))))

See also https://github.com/josteink/csharp-mode/issues/39#issuecomment-129636221. As suggested in the linked post, I'd generally recommend against writing such code and going for a top-level helper function instead. Emacs Lisp isn't Scheme after all, so you're only going to confuse whoever may read your code...

  • Thanks, it looks this is a very straightforward but correct solution. There is an issue with defun: once nats is called, inner is globally accessible, I guess makunbound can be used to fix it. – xuchunyang Sep 11 '16 at 11:25
  • Huh, that shouldn't happen. Either I'm wrong about this solution or there's a bug in the lexical binding code... – wasamasa Sep 11 '16 at 11:55
  • Anyway, I've replaced it with an alternative solution that doesn't require cl-lib. – wasamasa Sep 11 '16 at 12:06
  • 1
    defun always makes global bindings, it expands to defalias which is like fset. letrec is the right choice here. – npostavs Sep 11 '16 at 13:18
  • 2
    +1 (especially for the recommendation and reminder that Emacs Lisp is not Scheme). – Drew Sep 11 '16 at 17: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.