Higher-Order Functions and Closures

;; Closures capture lexical variables
(defun make-counter (&optional (start 0))
  (let ((count start))
    (lambda () (incf count))))

(defparameter *c1* (make-counter))
(funcall *c1*)                           ; 1
(funcall *c1*)                           ; 2

(defun make-account (balance)
  (list :deposit (lambda (amt) (incf balance amt))
        :withdraw (lambda (amt) (if (>= balance amt) (decf balance amt) (error "Insufficient funds")))
        :balance (lambda () balance)))
(defparameter *acc* (make-account 100))
(funcall (getf *acc* :deposit) 50)       ; 150

;; Function composition and partial application
(defun compose (&rest fns)
  (if (null fns)
      #'identity
      (let ((f (first fns)) (g (apply #'compose (rest fns))))
        (lambda (&rest args) (funcall f (apply g args))))))
(funcall (compose #'1+ #'abs) -5)        ; 6

(defun curry (fn &rest args1)
  (lambda (&rest args2) (apply fn (append args1 args2))))
(funcall (curry #'+ 10) 5)               ; 15
(mapcar (curry #'* 2) '(1 2 3))          ; (2 4 6)
;; Alexandria provides compose, curry, rcurry, conjoin, disjoin

(complement #'evenp)                     ; a function returning the opposite
(remove-if (complement #
…