I think the problem lies in the lisp printer, the implementation is showing us exactly the same thing for both things, while internally it seems it's really a list vs a function.
The usual and confusing pattern shows:
<i>[hao@wendy:~]$ ecl
;;; Loading "/nix/store/j48bf40ssnzgil3qmdc759y0navk079d-ecl-readline/lib/init.lsp"
n;;; Loading "/nix/store/j48bf40ssnzgil3qmdc759y0navk079d-ecl-readline/lib/ecl-readline.fas"
ix-;;; Loading "/nix/store/j48bf40ssnzgil3qmdc759y0navk079d-ecl-readline/lib/ecl-completions.fas"
sECL (Embeddable Common-Lisp) 16.1.3 (git:UNKNOWN)
Copyright (C) 1984 Taiichi Yuasa and Masami Hagiya
Copyright (C) 1993 Giuseppe Attardi
Copyright (C) 2000 Juan J. Garcia-Ripoll
Copyright (C) 2016 Daniel Kochmanski
ECL is free software, and you are welcome to redistribute it
under certain conditions; see file 'Copyright' for details.
Type :h for Help.
Top level in: #<process TOP-LEVEL>.
h+CL-USER[1]> (macroexpand '(lambda (a) (+a 12)))
#'(LAMBDA (A) (+A 12))
T
+CL-USER[2]> (equal (macroexpand '(lambda (a) (+ a 12))) '(lambda (a) (+ a 12)))
NIL
+CL-USER[3]> (map 'list #'type-of (list (macroexpand '(lambda (a) (+ a 12))) '(lambda (a) (+ a 12))))
(CONS CONS)
+CL-USER[4]> (map 'list #'functionp (list (macroexpand '(lambda (a) (+ a 12))) '(lambda (a) (+ a 12))))
(NIL NIL)
+CL-USER[5]> (map 'list #'car (list (macroexpand '(lambda (a) (+ a 12))) '(lambda (a) (+ a 12))))
#'LAMBDA
And it seems the only difference is just a hashquote
?
+CL-USER[8]> (ignore-errors (list *print-escape* *print-readably* *print-case* *print-circle* *print-level* *print-length* *print-pretty*))
(T NIL :UPCASE NIL NIL NIL T)
+CL-USER[9]> (let ((*print-readably* t)) (list (macroexpand '(lambda (a) (+ a 12))) '(lambda (a) (+ a 12)))))
(#'(LAMBDA (A) (+ A 12)) (LAMBDA (A) (+ A 12)))
;;; Warning: Ignoring an unmatched right parenthesis.
+CL-USER[10]> (setq *print-pretty* nil)
NIL
+CL-USER[11]> (setq the-funs (list (macroexpand '(lambda (a) (+ a 12))) '(lambda (a) (+ a 12))))
(#'(LAMBDA (A) (+ A 12)) (LAMBDA (A) (+ A 12)))
+CL-USER[12]> *print-pretty*
NIL
I was trying to force the printer to explicitly show the (function (lambda ...))
part of the definition [22], but i cant remember the last time i was doing exactly the same thing trying to find out why the subtetly arised just when starting to gain trust on lisp
+CL-USER[21]> (type-of (car the-funs)))
CONS
;;; Warning: Ignoring an unmatched right parenthesis.
+CL-USER[22]> (car (car the-funs))
FUNCTION
What we are seing actually is a list/cons (below) and a function (above)
+CL-USER[23]> (values #1=(cadr the-funs) (type-of #1#))
(LAMBDA (A) (+ A 12))
CONS
And maybe the difference isn't just visually a printed character; Here's SBCL:
<i>[hao@wendy:~]$ sbcl
This is SBCL 2.0.0.nixos, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
* (setq fs (list (macroexpand '(lambda (x) (1+ x))) '(lambda (x) (1+ x))))
; in: SETQ FS
; (SETQ FS (LIST (MACROEXPAND '(LAMBDA # #)) '(LAMBDA (X) (1+ X))))
;
; caught WARNING:
; undefined variable: COMMON-LISP-USER::FS
;
; compilation unit finished
; Undefined variable:
; FS
; caught 1 WARNING condition
(#'(LAMBDA (X) (1+ X)) (LAMBDA (X) (1+ X)))
* (let (*print-pretty*) (print (lambda (x) (1+ x))))
#<FUNCTION (LAMBDA (X)) {52B1CABB}>
#<FUNCTION (LAMBDA (X)) {52B1CABB}>
* (let (*print-pretty*) (print ' (lambda (x) (1+ x))))
(LAMBDA (X) (1+ X))
(LAMBDA (X) (1+ X))
The quote showed more explicitly here
#<FUNCTION (LAMBDA (X)) {52B1CABB}>
(LAMBDA (X) (1+ X))
The tree, and the types are somewhat similar to what ECL said.
* (mapcar #'functionp fs)
(NIL NIL)
* (mapcar #'type-of fs)
(CONS CONS)
* (mapcar #'car fs)
#'LAMBDA
* (values (car (car fs)) (car (cdr fs)))
FUNCTION
(LAMBDA (X) (1+ X))
Finally, (dont be taking) a random tree walk through the forest confirms that we can walk the list, but the function is just closured?
* (defun walk-tree (fun tree)
(subst-if t
(constantly nil)
tree
:key fun))
WALK-TREE
* (walk-tree 'print (lambda (x) (1+ x)))
#<FUNCTION (LAMBDA (X)) {52B1CD5B}>
#<FUNCTION (LAMBDA (X)) {52B1CD5B}>
* (walk-tree 'print '(lambda (x) (1+ x))))
(LAMBDA (X) (1+ X))
LAMBDA
((X) (1+ X))
(X)
X
NIL
((1+ X))
(1+ X)
1+
(X)
X
NIL
NIL
(LAMBDA (X) (1+ X))
* (defun walk-tree-atoms (fun tree)
(tree-equal tree tree
:test (lambda (element-1 element-2)
(declare (ignore element-2))
(funcall fun element-1)
t)))
WALK-TREE-ATOMS
* (walk-tree-atoms 'print (lambda (x) (1+ x)))
#<FUNCTION (LAMBDA (X)) {52B1CF9B}>
T
* (walk-tree-atoms 'print '(lambda (x) (1+ x)))
LAMBDA
X
NIL
1+
X
NIL
NIL
T
* (quit)
<i>[hao@wendy:~]$
The walking functions were taken from LispTips, which sadly seems to be taken down, i just had a flashback when i read this, you can still look the archive here:
https://web.archive.org/web/20191204131626/https://lisptips.com/post/43404489000/the-tree-walkers-of-cl
You can also look at the whole session, i pasted it in a gist:
https://gist.github.com/LaloHao/fd6499b68cc98cf440aad6447ebd9b89