These two forms may be similar on the surface, but they are very different in terms of the underlying mechanisms invloved. In a sense, Function
represents the only true (but leaky) functional abstraction in Mathematica. Functions based on rules are not really functions at all, they are global versions of replacement rules, which look like function calls.
One big difference is in the semantics of parameter-passing. For rules (and therefore functions based on rules), it is more intruding, in the sense that they don't care about the inner scoping constructs, while Function
(with named arguments only) will care. Here is an example of what I mean:
Function[{x},Module[{x=2},x]][3]
(*
==> 2
*)
while
In[46]:=
ClearAll[fn,a];
fn[x_]:=Module[{x=2},{Hold[x],x}]
fn[3]
During evaluation of In[46]:= Module::lvset: Local variable specification
{3=2} contains 3=2, which is an assignment to 3; only assignments to symbols are allowed. >>
Out[48]= Module[{3=2},{Hold[3],3}]
and especially
In[49]:=
Clear[a];
fn[a]
Out[50]= {Hold[a$840],2}
Another difference I want to mention is that, due to Function
with names arguments beaing a leaky abstraction, passing it as an argument to another function is risky. In addition to the mentioned above post by @WReach, which exposes the essence of the problem, see this post for an example of a trouble that one can get into, in a more "realistic" situation.
That said, in many cases one may think of "functions" defined by rules as normal functions. Because of the powerful builtin rule - dispatching mechanism of rule-application, functions based on rules can be overloaded in very powerful ways, which adds expressive power, as noted by @Sal.
Additionally, while this is not often used, one can often use global pattern-based function definitions as local rules, to achieve rather non-trivial things. In this answer, I use this technique to illustrate how one can dynamically generated Mathematica code at run-time, with the ability to test it in the "interpreter" mode. To illusrate the power of this approach here, I will write a (simplistic, sloppy) macro which will remove the Map
auto-compilation limitation mentioned by @Mike Honeychurch, at least for simple pattern-based functions:
ClearAll[withGlobalFunctions];
SetAttributes[withGlobalFunctions, HoldAll];
withGlobalFunctions[code_] :=
With[{rules =
Flatten@Cases[
Names["Global`*"],
s_ :> ToExpression[s, InputForm, DownValues]]
},
ReleaseHold[
Hold[code] //. {
HoldPattern[Map[f_Symbol, args__]] :> Map[f[#] &, args],
Sequence @@ rules
}
]
];
Now, taking the same setup:
data = RandomReal[{0, 10}, {500000}];
In[114]:=
Timing[Developer`PackedArrayQ[Map[square,data]]]
Timing[Developer`PackedArrayQ[tmp1=Map[square1,data]]]
Timing[withGlobalFunctions@Developer`PackedArrayQ[tmp2=Map[square2,data]]]
Out[114]= {0.031,True}
Out[115]= {0.015,True}
Out[116]= {0.032,True}
where what happened is that my macro has expanded the call to square2
before Map
was used on it. This is quite non-trivial, becuase it was able to expand the cal square2[#]
inside a pure function. This is actually the case of a constructive use of the mentioned above leaky functional abstraction - were Function
a complete black box, and this would not be possible. Note that the transformation f->f[#]&
is not always correct, since it leaks evaluation. I used it here as an example, but if one were to do it for real, more care must be taken.
fib = (If[#1 == 1 || #1 == 2, 1, #0[#1 - 1] + #0[#1 - 2]]) &
. – J. M.♦ Jan 26 '12 at 1:10