lambda
seems to have an "inherent" progn
as well, such that (funcall (lambda (x y z) A B C) 1 2 3)
is equivalent to (let ((x 1) (y 2) (z 3)) A B C)
.
"Equivalent" is a strong word. Calling a lambda
with a set of arguments may have a similar effect to evaluating the lambda
's body with the appropriate let
bindings in place, but their corresponding semantics are quite different.
a) What are the identifies between lambda
, let
and progn
?
Are you asking how they relate/compare to one another?
progn
is pure sequential evaluation of Lisp forms
let
is the same but with local variable binding at the start and unbinding at the end
lambda
evaluates to an anonymous function under dynamic binding, and
a closure under
lexical-binding
how would you implement lambda
and progn
just using let
?
That depends on what you mean by "implement using let
". You can't use let
alone to define a function, macro, or special form that will do the same thing as either lambda
or progn
, but some rewriting is possible in the case of progn
:
;; Expressions of this form:
(progn BODY...)
;; can generally be written as:
(let () BODY...)
No such rewrite is possible for anonymous functions and closures in general, because their evaluation semantics and byte-compilation are quite different to those of let
.
;; For example, this expression:
(lambda (x) (1+ x))
;; is not, under dynamic binding, the same as:
(list 'lambda '(x) '(1+ x))
;; and not, under lexical binding, the same as:
(list 'closure '(t) '(x) '(+ x y))
b) Is (progn (progn (A) (B) (C)))
always equivalent to (progn ((A) (B) (C)))
?
No, those two forms are never equivalent.
(progn (progn (A) (B) (C)))
is valid Elisp which evaluates (A)
, (B)
, and (C)
in that order, and returns the result of evaluating (C)
.
(progn ((A) (B) (C)))
is valid if and only if (A)
is a valid (lambda ...)
expression, as the first element of an evaluated list must already be (not evaluate to) a valid function object. In that case, (B)
and (C)
are evaluated and passed as arguments to the anonymous function/closure (A)
, whose result is then returned by progn
.
(I have the faint impression that expressions of the form ((lambda ...))
are meant to eventually be outlawed in Elisp, but I'm not sure.)
Update from Comments
In asking b), I meant to ask about the semantics and evaluation of the "implicit" progn
in let
/lambda
and an explicit progn
in their body, i.e. how does (let () (progn BODY...))
work?
(let () (progn BODY...))
is effectively the same as (progn (progn BODY...))
, which is effectively the same as (progn BODY...)
, i.e. adding an explicit progn
where there is already an implicit one is practically redundant/idempotent.
The special form let
is actually implemented in terms of progn
; see src/eval.c
:
DEFUN ("let", Flet, Slet, 1, UNEVALLED, 0,
doc: /* Bind variables according to VARLIST then eval BODY.
The value of the last form in BODY is returned.
Each element of VARLIST is a symbol (which is bound to nil)
or a list (SYMBOL VALUEFORM) (which binds SYMBOL to the value of VALUEFORM).
All the VALUEFORMs are evalled before any symbols are bound.
usage: (let VARLIST BODY...) */)
(Lisp_Object args)
{
/* Bind local variables according to first argument. */
Lisp_Object varlist = XCAR (args);
ptrdiff_t count = SPECPDL_INDEX ();
/* ...magic happens here... */
/* Pass remaining arguments to 'progn'. */
Lisp_Object elt = Fprogn (XCDR (args));
/* Unbind local variables; return result of 'progn'. */
return SAFE_FREE_UNBIND_TO (count, elt);
}
Note that idiomatic Elisp avoids redundant nesting of progn
where possible.