(compose) in Common Lisp
Asked Answered
E

2

8

We find this function builder to realize composition in P.Graham's "ANSI Common Lisp" (page 110). The arguments are n>0 quoted function names. I don't understand it completely, so I'll quote the code here and specify my questions underneath it:

(defun compose (&rest fns)
  (destructuring-bind (fn1 . rest) (reverse fns)
    #'(lambda (&rest args)
        (reduce #'(lambda (v f) (funcall f v)) 
                rest
                :initial-value (apply fn1 args)))))

The argument list to compose is reversed and unpacked, its (now first) element bound to 'fn1' and the rest to 'rest'. The body of the outermost lambda is a reduce: (funcall fi (funcall fi-1 ... ) ), with operands in inverted order to restore the initial one.

1) What is the role of the outermost lambda expression? Namely, where does it get its 'args' from? Is it the data structure specified as the first argument of destructuring-bind? 2) Where does the innermost lambda take its two arguments from?

I mean I can appreciate what the code does but still the lexical scope is a bit of a mystery to me. Looking forward to any and all comments! Thanks in advance, //Marco

Ezarra answered 17/10, 2013 at 11:4 Comment(10)
Why all of the hanging brackets?Iain
My apologies, I didn't see the discussion on this at #5928606Ezarra
@Iain , to guide the eye ;) is it bad practice? I'm still a noobEzarra
What is 'Dylan Lisp'?Antony
It could be the first version of apple dylan with its sexpr syntaxGeld
Most lisp users find it easier to read with all of the brackets at the end.Iain
Marco Zocca, @Marcin's point, even more concisely, is that most Lisp users read the indentation, not the brackets.Holytide
@Patrick: but above is not 'Dylan Lisp', which does not exist. Nothing in the question seems to have to do with Dylan. Not even the prefix version. The source code is not Dylan. The source code looks like Common Lisp.Antony
@Rainer: my bad, this is a CL implementation of a Dylan function, apparentlyEzarra
Possible duplicate of Compose example in Paul Graham's ANSI Common LispPapillary
D
11

It's probably easier if you consider first a couple of practical examples:

(defun compose1 (a)
  (lambda (&rest args)
    (apply a args)))

(defun compose2 (a b)
  (lambda (&rest args)
    (funcall a (apply b args))))

(defun compose3 (a b c)
  (lambda (&rest args)
    (funcall a (funcall b (apply c args)))))

So the outermost lambda is the return value: a function that takes any arguments, what it does with it is applying the last function and chaining all the others in reverse order on the result got from last function.

Note: compose1 could be defined more simply as (defun compose1 (a) a).

A somewhat equivalent but less efficient version could be

(defun compose (&rest functions)
  (if (= (length functions) 1)
      (car functions)
      (lambda (&rest args)
        (funcall (first functions)
                 (apply (apply #'compose (rest functions))
                        args)))))
Dolomite answered 17/10, 2013 at 12:0 Comment(1)
Thank you for the examples and the explanation, this definitely clarifies matters!Ezarra
G
2

1) The outermost lambda creates a closure for you, because the result of (combine ...) is a function that calulates the composition of other functions.

2) The innermost lambda gets ists argument from the function reduce. Reduce takes a function (the innermost lambda) of two arguments and applies it stepwise to a list, e.g.

 (reduce #'- '(1 2 3 4))  is   (- (- (- 1 2) 3) 4)
Geld answered 17/10, 2013 at 11:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.