Preamble
There are some important differences (or, more precisely, features of Function
which can't be reproduced with symbols and rules), that have not been reflected in answers here, but that I think deserve a separate answer. These are related to some more advanced uses, involving evaluation control, closures, and garbage collection.
Emulating Hold
attributes for SubValue - type definitions
Normally, you can't hold arguments sub__
in a call
fn[args___][sub___]
if your definitions are given as SubValues
, like
fn[args___][sub___]:=Hold[args, sub]
because of the way evaluation process work. However, you can define instead
fn[args___]:=Function[##,Hold[args,##],HoldAll]
which would effectively also hold sub
. You can't make this work without using a pure function, AFAIK.
Closures and symbol management
Basically, Function
is indispensable for creating (nested) closures, and the reason for that is that it spares you from manual symbol management, since there are no explicit names / symbols, to which the action is attached.
It is not very easy to find a good example, because the style of programming based on closures is not very widely used in Mathematica. I will use some very simplified example of what I needed to do at some point. Imagine that you have some data, which you want to split to chunks and present in a form, in which you can apply some transformations to that chunked data in a delayed (lazy) fashion, so that they are only carried out when some specific chunk of data is requested. Here is one way to create such a construct:
ClearAll[lazyListMake]
lazyListMake[data_List, size_] :=
With[{len = Length[data]},
LList[
Function[index,
Function[
Take[data, {
Min[len, size* (index - 1) + 1],
Min[len, size*index]
}]
]]]]
and the data extractor:
ClearAll[getData];
getData[LList[fun_], index_] := fun[index][];
Here is how you can use it:
ll = lazyListMake[Range[30], 5];
getData[ll, 3]
(* {11, 12, 13, 14, 15} *)
The advantage of the nested function construction above is that the inner function serves as a pointer to the data - I can operate on it without extracting the data itself. Now, I can implement a lazy Map
operation, for example:
LList /: Map[f_, LList[fun_]] :=
LList[
Function[index,
Composition[
Function[data, Map[f, data]],
fun[index]
]
]]
You can see that this version of Map
isn't doing anything when applied, it just transforms the inner function (a "pointer" to actual piece of data), so that Map
would actually be applied when we extract the data.
Indeed, we can do:
llm = Map[#^2 + 1 &, ll];
llm1 = Map[Sin, llm];
and no actual work was yet done by Map
. When we extract the data, we get:
getData[llm1, 3]
(* {Sin[122], Sin[145], Sin[170], Sin[197], Sin[226]} *)
and both Map
operations only executed now, when we extracted the data, and only on a specific piece of data we wanted.
Returning back to the original question, the above functionality does not need any symbol management, since all functions used here were pure functions. This is a big advantage. This means, in particular, that we can pass expressions involving so constructed lists (LL[...]
) anywhere we like, can construct a large number of them, if needed - and in all cases, the destruction of their inner state (when they are no longer used / referenced) is handled automatically by the garbage collector.
Garbage collection of Module
- generated variables captured by a closure
Another very interesting, and in fact useful, related feature is that when we leak Module
- variables into the closures (creating closures with a state), then those leaked Module
variables are garbage-collected once the closure is no longer referenced.
Let's start by executing (on a fresh kernel)
$HistoryLength = 0;
Now consider:
myFun = Module[{y = 1}, Function[var, var + y++]]
(* Function[var$, var$ + y$324++] *)
where we can see that we have now a specific Module
- generated variable y$324
as a part of the function's body. Each time we call this function on some argument, that variable gets incremented:
myFun /@ {1, 2, 3}
(* {2, 4, 6} *)
So, we have constructed a closure that was closed over a mutable variable. We can inspect the names:
Names["Global`*"]
(* {"myFun", "var", "var$", "y", "y$324"} *)
to confirm that the variable is in fact visible on the top level. Now, let us call Remove
on myFun
variable, which stores (references) the functions, and inspect the names of global variables again:
Remove[myFun];
Names["Global`*"]
(* {"var", "var$", "y"} *)
What you see is that the variable y$324
has been garbage - collected. Which is exactly the behavior we want.
The myFun
variable was a proxy, which I used to illustrate the mechanism. In practice, it won't be there - we would just pass the pure function directly. So, as soon as it's no longer a part of any expression, the leaked variables will be automatically garbage-collected. Now, should we use named symbols, and that wouldn't be the case, simply because for named symbols, there isn't any automatic garbage collection, so we'd have to do it manually. This means that not only those symbols used for function names would hang around, but also all internal stateful symbols, in such a case.
Summary
I tried to illustrate a few less obvious advantages of having pure functions (Function
) in the language, related to the uses of closure and garbage collection.
fib = (If[#1 == 1 || #1 == 2, 1, #0[#1 - 1] + #0[#1 - 2]]) &
. $\endgroup$