destructuring-bind
combines destructors with binding. A destructor is a function that lets you access a part of a data structure. car
and cdr
are simple destructors to extract the head and tail of a list. getf
is a general destructor framework. Binding is most commonly performed by let
. In this example, fns
is (#'list #'round #'sqrt)
(the arguments to compose
), so (reverse fns)
is (#'sqrt #'round #'list)
. Then
(destructuring-bind (fn1 . rest) '(#'sqrt #'round #'list)
...)
is equivalent to
(let ((tmp '(#'sqrt #'round #'list)))
(let ((fn1 (car tmp))
(rest (cdr tmp)))
...))
except that it doesn't bind tmp
, of course. The idea of destructuring-bind
is that it's a pattern matching construct: its first argument is a pattern that the data must match, and symbols in the pattern are bound to the corresponding pieces of the data.
So now fn1
is #'sqrt
and rest
is (#'round #'list)
. The compose
function returns a function: (lambda (&rest args) ...)
. Now consider what happens when you apply that function to some argument such as 4
. The lambda can be applied, yielding
(reduce #'(lambda (v f) (funcall f v))
'(#'round #'list)
:initial-value (apply #'sqrt 4)))
The apply
function applies fn1
to the argument; since this argument is not a list, this is just (#'sqrt 4)
which is 2
. In other words, we have
(reduce #'(lambda (v f) (funcall f v))
'(#'round #'list)
:initial-value 2)
Now the reduce
function does its job, which is to apply #'(lambda (v f) (funcall f v))
successively to the #'round
and to #'list
, starting with 2
. This is equivalent to
(funcall #'list (funcall #'round 2))
→ (#'list (#'round 2))
→ '(2)
(reduce ... (reverse fns) ...
should be written as(reduce ... fns :from-end t ...
– Finedraw