I wrote earlier about problems with Mathematica's pure functions. The problems make the abstraction somewhat leaky. Here are some more problems with that abstraction:
Contrast the return values of the following expressions:
Function[expr, With[{x = 1}, expr]][x]
(* x *)
Function[Null, With[{x = 1}, #]][x]
(* 1 *)
With[{x = 1}, #]&[x]
(* 1 *)
Tracing reveals that Function
is sometimes renaming variables:
Trace @ Function[expr,With[{x=1},expr]][x]
(* {Function[expr,With[{x=1},expr]][x], With[{x$=1},x], x} *)
Trace @ Function[Null,With[{x=1},#]][x]
(* {Function[Null,With[{x=1},#1]][x], With[{x=1},x], 1} *)
This renaming of variables within enclosed scoping constructs is somewhat erratic. It happens if the function arguments are named but not if the arguments are anonymous. The differences in behaviour can be exploited to good effect, to be sure, but they are certainly not intuitive. It is not clear to me which of these behaviours is "correct". But it is even more unclear why the behavioural difference is triggered by the naming of function arguments. This is another example of subtle Mathematica behaviour for beginners to trip over -- and experienced users too.
Function
with named arguments will rename variables in inner With
and Module
constructs, but not Block
constructs. The exception for
Block
makes sense given that it does not actually introduce any new variables, it just
overrides the definitions of existing ones. However, Function
with anonymous arguments
does not rename variables in any of these constructs.
It is possible to "trick" Function
so that it does not rename variables of enclosed scoping
constructs. The trick is to disguise those constructs. For example:
Function[expr, With@@Hold[{x = 1}, expr]][x]
(* 1 *)
In this case, Function
does not spot the inner With
since it is not generated
until a later evaluation.
Note that Function
does not rename (non-pattern) symbols in replacement expressions, irrespective of whether
the function arguments are named or not. For example, both of the following expressions return the same
result:
Function[expr, expr /. x -> 1][x]
(* 1 *)
Function[Null, # /. x -> 1][x]
(* 1 *)
Maybe all of this behaviour can be divined from Variables in Pure Functions in the Mathematica help. But it didn't occur to me.