licorna
licorna

Reputation: 5870

Difference between (apply #'somefunc args) and (somefunc args)

While reading Paul Graham's On Lisp I found the following function in Chapter 4, Utility Functions.

(defun symb (&rest args)
  (values (intern (apply #'mkstr args)))) ;; mkstr function is "applied"
;; which evaluates like in the following example:
> (symb nil T :a)
NILTA

I would like to understand what is the difference with the following function, slightly different:

(defun symb1 (&rest args)
  (values (intern (mkstr args)))) ;; directly calling mkstr
;; which evaluates like in the following example:
> (symb1 nil T :a)
|(NIL T A)|

In this second version, mkstr is directly evaluated with args arguments, but I don't understand why we need to do (apply #'mkstr ...) in the original.

Upvotes: 0

Views: 119

Answers (3)

Rainer Joswig
Rainer Joswig

Reputation: 139251

The purpose of APPLY is to call functions with computed argument lists.

Imagine the user enters some arguments and we want to call the function WRITE. WRITE takes a lot of possible arguments. The first argument is the object to print the rest are keyword value options:

possible keyword arguments for WRITE:

array base case circle escape gensym
length level lines miser-width pprint-dispatch
pretty radix readably right-margin stream

Let's read the argument list as a list using READ and call WRITE via APPLY with the argument list:

CL-USER 30 > (loop for input = (read)
                   while input
                   do
                   (format t "~%# ")
                   (apply #'write input)
                   (format t "~%~%"))
((1 5 10 30 55 26 12 17))
# (1 5 10 30 55 26 12 17)

((1 5 10 30 55 26 12 17) :base 16)
# (1 5 A 1E 37 1A C 11)

((1 5 10 30 55 26 12 17) :base 12)
# (1 5 A 26 47 22 10 15)

((1 5 10 30 55 26 12 17) :length 5)
# (1 5 10 30 55 ...)

((1 5 10 30 55 26 12 17) :base 16 :length 5)
# (1 5 A 1E 37 ...)

Another way to achieve something similar would have been using EVAL.

CL-USER 35 > (let ((f #'+)
                   (args '(20 22)))
               (eql (eval (list* 'funcall f args))
                    (apply f args)))
T

Upvotes: 4

Renzo
Renzo

Reputation: 27424

Let’s look at the definition of mkstr:

CL-USER> (defun mkstr (&rest args)
           (with-output-to-string (s)
             (dolist (a args) (princ a s))))
MKSTR

it is a function that takes a variable number of arguments, of any type, packs them in a list, and assigns this list to the formal parameter args (due to the &rest specification of the parameter). Then, the function prints all the elements of this list with printc, producing a string that is the result of concatenating all the printed representations of them (without intervening spaces). So, for instance:

CL-USER> (mkstr '(a b c))
"(A B C)"
CL-USER> (mkstr 3 'A '(A b 4))
"3A(A B 4)"

Similarly, the functions symb and symb1 take a variable numbers of arguments and args will contain the list formed by them. So, symb1 calls mkstr with a single argument, the list of the arguments passed to symb1, so that mkstr creates a unique string from the list, and finally the list is interned to transform it in an atom. In symb, instead, the function mkstr is applied to all the arguments extracted from the list, since apply is used (see the specification), so that all the elements of the list are concatened together and then transformed into an atom..

Upvotes: 2

coredump
coredump

Reputation: 38799

When you call (f args), you call f with one argument.

With (apply #'f args), you call f with as many arguments as the args list contains. So if args is (1 2), then (apply #'f args) is equivalent to (f 1 2).

See APPLY.

Upvotes: 4

Related Questions