1

I'm going through Paul Graham's On Lisp, and trying to implement the functions in Emacs Lisp. One of them is flatten :

(flatten '(a (b c) ((d e) f)))
;; Returns:
(a b c d e f)

Yet for some reason, the implementation given by Paul Graham does not work on Emacs Lisp (always returns nil):

(defun flatten (x)
  (cl-labels ((rec (x acc))
              (cond ((null x) acc)
                    ((atom x) (cons x acc))
                    (t (rec (car x) (rec (cdr x) acc)))))
             (rec x nil)))

(flatten '(1 (3)))
;; Returns:
nil

Does it have something to do with ELisp's dynamic binding? What's wrong with this code?

Community
  • 1
  • 1
kotchwane
  • 2,082
  • 1
  • 19
  • 24
  • Drop the last closing paren from `((rec (x acc))` since that just defines `rec` to be function returning `nil`, changing it to `((rec (x acc)`, and then put the closing paren at the end of the whole `cond` expression so that `cond` becomes the body of `rec`. – Steve Vinoski Jun 23 '19 at 13:23
  • Ouch. Thank you, you nailed it. Copy-paste is not possible from the book, and even though I checked ten times the syntax, I missed the paren. Tried to debug, but debugging on labels is not easy for a beginner. Syntax is : (cl-labels ((FUNC ARGLIST BODY...) ...) FORM...) Question answered. – kotchwane Jun 23 '19 at 14:14
  • 1
    By the way, Emacs doesn't implement tail-call optimization. – npostavs Jul 24 '19 at 17:34

1 Answers1

1

As noted in my comment to the question, the problem is a misplaced parenthesis. The definition should be:

(defun flatten (x)
  (cl-labels ((rec (x acc)
                   (cond ((null x) acc)
                         ((atom x) (cons x acc))
                         (t (rec (car x) (rec (cdr x) acc))))))
    (rec x nil)))

In the original, ((rec (x acc)) defines rec as a function returning nil. By changing it to ((rec (x acc) the cond expression becomes the body of rec, and then after balancing the parentheses again by adding a closing parenthesis after the t clause of the cond, the flatten function works as expected:

(flatten '(1 (3)))
(1 3)
Steve Vinoski
  • 19,847
  • 3
  • 31
  • 46