... until the collector arrives ...

This "blog" is really just a scratchpad of mine. There is not much of general interest here. Most of the content is scribbled down "live" as I discover things I want to remember. I rarely go back to correct mistakes in older entries. You have been warned :)

2012-03-06

More Leaks in Mathematica's Function Abstraction

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.

Blog Archive