Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mapcan, sharp quote and closures

I'm somewhat new to CL and currently trying to wrap my head around mapcan, #', funcall and closures. Here is a closure, which applies a predicate to a number n and, if correct, returns (list n), else nil:

(defun all-those (predicate)
  (lambda (n)
    (if (funcall predicate n) (list n))))

I understand that I need to call funcall to turn this closure into a function. This works fine:

>  (funcall (all-those #'evenp) 8)
(8)

Now I tried to pass the hereby created function as an argument to mapcan:

>  (mapcan #'(funcall (all-those #'evenp)) '(1 2 3 4))

I get a compile-time-error: (FUNCALL (ALL-THOSE #'EVENP)) is not a legal function name.

But it works if I omit #' as well as funcall:

>  (mapcan (all-those #'evenp) '(1 2 3 4))
(2 4)

Now I'm confused. It was my understanding that I need to sharp-quote a function when using mapcan to follow the symbol's function binding (*) and that I need to call funcall when "closing a closure".

Is it because #' and funcall are cancelling each other out or why do I have to omit both of them in the above example? Thank you in advance for any replies.


(*) I know that in this example I don't really have a symbol whose function binding can be followed. But if I use an anonymous function and mapcan I still need to sharp-quote it: (mapcan #'(lambda ...

like image 931
Frank Avatar asked Nov 10 '16 21:11

Frank


1 Answers

To mapcar, funcall, etc., you need to pass either a function object or a symbol. If you pass a symbol, then the symbol-function of the symbol is used as the function. If you pass a function object, then it is used as the function.

Your all-those function returns a function. That means that (mapcan (all-those …) …) is fine.

The sharp quote (#') is just shorthand for the function form. That is, #'foo is the same as (function foo):

The value of function is the functional value of name in the current lexical environment.

If name is a function name, the functional definition of that name is that established by the innermost lexically enclosing flet, labels, or macrolet form, if there is one. Otherwise the global functional definition of the function name is returned.

If name is a lambda expression, then a lexical closure is returned. In situations where a closure over the same set of bindings might be produced more than once, the various resulting closures might or might not be eq.

So you only use #' or function with a function name. That means either a symbol (e.g., #'car) or a lambda expression (e.g., #'(lambda (x) x)). That means that the following doesn't work (or really make sense, even):

#'(funcall (all-those #'evenp))

Now I'm confused. It was my understanding that I need to sharp-quote a function when using mapcan to follow the symbol's function binding (*) and that I need to call funcall when "closing a closure".

The documentation for mapcar, etc., says that its first argument is:

function---a designator for a function that must take as many arguments as there are lists.

From the glossary:

function designator n. a designator for a function; that is, an object that denotes a function and that is one of: a symbol (denoting the function named by that symbol in the global environment), or a function (denoting itself). The consequences are undefined if a symbol is used as a function designator but it does not have a global definition as a function, or it has a global definition as a macro or a special form. See also extended function designator.

So, you can pass a function directly to mapcar, funcall, etc., which is exactly what you're doing in:

(mapcan (all-those …) …)

You can also do:

(mapcan (lambda (x) ...) ...)
(mapcan #'(lambda (x) ...) ...)
(mapcan 'car ...)
(mapcan #'car ...)
(mapcan (symbol-function 'car) ...)
like image 120
Joshua Taylor Avatar answered Oct 13 '22 04:10

Joshua Taylor