Functions as first class values

Syntax

  • (function name) ; retrieves the function object of that name
  • #'name ; syntactic sugar for (function name)
  • (symbol-function symbol) ; returns the function bound to symbol
  • (funcall function args...) ; call function with args
  • (apply function arglist) ; call function with arguments given in a list
  • (apply function arg1 arg2 ... argn arglist) ; call function with arguments given by arg1, arg2, ..., argn, and the rest in the list arglist

Parameters

ParameterDetails
namesome (unevaluated) symbol which names a function
symbola symbol
functiona function which is to be called
args...zero or more arguments (not a list of arguments)
arglista list containing arguments to be passed to a function
arg1, arg2, ..., argneach is a single argument to be passed to a function

Remarks

When talking about Lisp-like languages there is a common distinction between what is known as a Lisp-1 and a Lisp-2. In a Lisp-1, symbols only have a value and if a symbol refers to a function then the value of that symbol will be that function. In a Lisp-2, symbols can have separate associated values and functions and so a special form is required to refer to the function stored in a symbol instead of the value.

Common Lisp is basically a Lisp-2 however there are in fact more than 2 namespaces (things that symbols can refer to) -- symbols can refer to values, functions, types and tags, for example.

Closures

Functions remember the lexical scope they where defined in. Because of this, we can enclose a lambda in a let to define closures.

(defvar *counter* (let ((count 0))
                    (lambda () (incf count))))

(funcall *counter*) ;; => 1
(funcall *counter*) ;; = 2

In the example above, the counter variable is only accessible to the anonymous function. This is more clearly seen in the following example

(defvar *counter-1* (make-counter))
(defvar *counter-2* (make-counter))

(funcall *counter-1*) ;; => 1
(funcall *counter-1*) ;; => 2
(funcall *counter-2*) ;; => 1
(funcall *counter-1*) ;; => 3

Defining anonymous functions

Functions in Common Lisp are first class values. An anonymous function can be created by using lambda. For example, here is a function of 3 arguments which we then call using funcall

CL-USER> (lambda (a b c) (+ a (* b c)))
#<FUNCTION (LAMBDA (A B C)) {10034F484B}>
CL-USER> (defvar *foo* (lambda (a b c) (+ a (* b c))))
*FOO*
CL-USER> (funcall *foo* 1 2 3)
7

Anonymous functions can also be used directly. Common Lisp provides a syntax for it.

((lambda (a b c) (+ a (* b c)))    ; the lambda expression as the first
                                   ; element in a form
  1 2 3)                           ; followed by the arguments

Anonymous functions can also be stored as global functions:

(let ((a-function (lambda (a b c) (+ a (* b c)))))      ; our anonymous function
  (setf (symbol-function 'some-function) a-function))   ; storing it

(some-function 1 2 3)                                   ; calling it with the name

Quoted lambda expressions are not functions

Note that quoted lambda expressions are not functions in Common Lisp. This does not work:

(funcall '(lambda (x) x)
         42)

To convert a quoted lambda expression to a function use coerce, eval or funcall:

CL-USER > (coerce '(lambda (x) x) 'function)
#<anonymous interpreted function 4060000A7C>

CL-USER > (eval '(lambda (x) x))
#<anonymous interpreted function 4060000B9C>

CL-USER > (compile nil '(lambda (x) x))
#<Function 17 4060000CCC>

Defining functions that take functions and return functions

A simple example:

CL-USER> (defun make-apply-twice (fun)
           "return a new function that applies twice the function`fun' to its argument"
           (lambda (x)
             (funcall fun (funcall fun x))))
MAKE-APPLY-TWICE
CL-USER> (funcall (make-apply-twice #'1+) 3)
5
CL-USER> (let ((pow4 (make-apply-twice (lambda (x) (* x x)))))
           (funcall pow4 3))
81

The classical example of function composition: (fgh)(x) = f (g (h (x)):

CL-USER> (defun compose (&rest funs)
           "return a new function obtained by the functional compositions of the parameters"
           (if (null funs) 
               #'identity
               (let ((rest-funs (apply #'compose (rest funs))))
                 (lambda (x) (funcall (first funs) (funcall rest-funs x))))))
COMPOSE
CL-USER> (defun square (x) (* x x))
SQUARE
CL-USER> (funcall (compose #'square #'1+ #'square) 3)
100  ;; => equivalent to (square (1+ (square 3)))

Higher order functions

Common Lisp contains many higher order functions which are passed functions for arguments and call them. Perhaps the most fundamental are funcall and apply:

CL-USER> (list 1 2 3)
(1 2 3)
CL-USER> (funcall #'list 1 2 3)
(1 2 3)
CL-USER> (funcall #'list 1 2 3 4 5)
(1 2 3 4 5)
CL-USER> (apply #'list '(1 2 3))
(1 2 3)
CL-USER> (apply #'list 1 2 '(4 5))
(1 2 3 4 5)
CL-USER> (apply #'+ 1 (list 2 3))
6
CL-USER> (defun my-funcall (function &rest args)
           (apply function args))
MY-FUNCALL
CL-USER> (my-funcall #'list 1 2 3)
(1 2 3)

There are many other higher order-function which, for example, apply a function many times to elements of a list.

CL-USER> (map 'list #'/ '(1 2 3 4))
(1 1/2 1/3 1/4)
CL-USER> (map 'vector #'+ '(1 2 3 4 5) #(5 4 3 2 10))
#(6 6 6 6 15)
CL-USER> (reduce #'+ '(1 2 3 4 5))
15
CL-USER> (remove-if #'evenp '(1 2 3 4 5))
(1 3 5)

Implementing reverse and revappend

Common Lisp already has a reverse function, but if it didn't, then it could be implemented easily using reduce. Given a list like

(1 2 3) === (cons 1 (cons 2 (cons 3 '())))

the reversed list is

(cons 3 (cons 2 (cons 1 '()))) === (3 2 1)

That may not be an obvious use of reduce, but if we have a "reversed cons" function, say xcons, such that

(xcons 1 2) === (2 . 1)

Then

(xcons (xcons (xcons () 1) 2) 3)

which is a reduction.

(reduce (lambda (x y)
          (cons y x))
        '(1 2 3 4)
        :initial-value '())
;=> (4 3 2 1)

Common Lisp has another useful function, revappend, which is a combination of reverse and append. Conceptually, it reverses a list and appends it to some tail:

(revappend '(3 2 1) '(4 5 6))
;;=> (1 2 3 4 5 6)

This can also be implemented with reduce. It fact, it's the same as the implementation of reverse above, except that the initial-value would need to be (4 5 6) instead of the empty list.

(reduce (lambda (x y)
          (cons y x))
        '(3 2 1)
        :initial-value '(4 5 6))
;=> (1 2 3 4 5 6)

Referring to Existing Functions

Any symbol in Common Lisp has a slot for a variable to be bound and a separate slot for a function to be bound.

Note that the naming in this example is only for illustration. Global variables should not be named foo, but *foo*. The latter notation is a convention to make it clear that the variable is a special variable using dynamic binding.

CL-USER> (boundp 'foo) ;is FOO defined as a variable?
NIL
CL-USER> (defvar foo 7)
FOO
CL-USER> (boundp 'foo)
T
CL-USER> foo
7
CL-USER> (symbol-value 'foo)
7
CL-USER> (fboundp 'foo) ;is FOO defined as a function?
NIL
CL-USER> (defun foo (x y) (+ (* x x) (* y y)))
FOO
CL-USER> (fboundp 'foo)
T
CL-USER> foo
7
CL-USER> (symbol-function 'foo)
#<FUNCTION FOO>
CL-USER> (function foo)
#<FUNCTION FOO>
CL-USER> (equalp (quote #'foo) (quote (function foo)))
T
CL-USER> (eq (symbol-function 'foo) #'foo)
T
CL-USER> (foo 4 3)
25
CL-USER> (funcall foo 4 3)
;get an error: 7 is not a function
CL-USER> (funcall #'foo 4 3)
25
CL-USER> (defvar bar #'foo)
BAR
CL-USER> bar
#<FUNCTION FOO>
CL-USER> (funcall bar 4 3)
25
CL-USER> #'+
#<FUNCTION +>
CL-USER> (funcall #'+ 2 3)
5

Summing a list

The reduce function can be used to sum the elements in a list.

(reduce '+ '(1 2 3 4))
;;=> 10

By default, reduce performs a left-associative reduction, meaning that the sum 10 is computed as

(+ (+ (+ 1 2) 3) 4)

The first two elements are summed first, and then that result (3) is added to the next element (3) to produce 6, which is in turn added to 4, to produce the final result.

This is safer than using apply (e.g., in (apply '+ '(1 2 3 4)) because the length of the argument list that can be passed to apply is limited (see call-arguments-limit), and reduce will work with functions that only take two arguments.

By specifying the from-end keyword argument, reduce will process the list in the other direction, which means that the sum is computed in the reverse order. That is

(reduce '+ (1 2 3 4) :from-end t)
;;=> 10

is computing

(+ 1 (+ 2 (+ 3 4)))


2016-07-21
2016-09-11
common-lisp Pedia
Icon