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
  • 1
    If you want a recursive local function, you can use letrec (a newish Elisp construct) or cl-labels.
    – Stefan
    Commented Sep 12, 2016 at 19:47

1 Answer 1

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...

7
  • 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
    Commented Sep 11, 2016 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
    Commented Sep 11, 2016 at 11:55
  • Anyway, I've replaced it with an alternative solution that doesn't require cl-lib.
    – wasamasa
    Commented Sep 11, 2016 at 12:06
  • 1
    defun always makes global bindings, it expands to defalias which is like fset. letrec is the right choice here.
    – npostavs
    Commented Sep 11, 2016 at 13:18
  • 2
    +1 (especially for the recommendation and reminder that Emacs Lisp is not Scheme).
    – Drew
    Commented Sep 11, 2016 at 17:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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