Is anonymous pure function a scoping construct?

In recent thread was raised the question: why anonymous pure functions Function[body] (or body &) do not rename symbols in nested scoping constructs while pure functions with named parameters Function[{vars}, body] do rename them as seen from the following example:

lhs_ :> # &@arg
Function[rhs, lhs_ :> rhs]@arg

lhs_ :> arg

lhs$_ :> arg  (in the second case lhs is renamed to lhs$).

The provided explanation (first given in the comment) states that pure function with no named arguments isn't a scoping construct, hence localization of variables in the nested scope isn't performed. This looks as kind of obvious since there is no need to localize variables inside of a construct which doesn't use variables itself (anonymous pure functions use only Slot).

But when trying to find where it is stated in the official Documentation, I was confused: the modern Documentation seems to state the opposite (although all the linked examples are only about the form Function[{vars}, body]), emphasis is mine:

Function constructs can be nested in any way. Each is treated as a scoping construct, with named inner variables being renamed if necessary. »

At the same time Leonid Shifrin notes in his book "Mathematica programming: an advanced introduction" (emphasis is mine):

It is important to note that there is no fundamental difference between functions defined with the # - & notation and functions defined with the Function command, in the sense that both definitions produce pure functions. There are however several technical differences that need to be mentioned.

The first one is that the Function[{vars},body] is a scoping construct, similar to Module, Block, With etc.

what implies that only the form Function[{vars},body] is a scoping construct, not the form defined with the # - & notation.

Let us make the things clear: is the form Function[body] (and equivalent forms body & and Function[Null, body]) a scoping construct or not? I ask both for authoritative references and for rational analysis of the situation.

It seems to me that the Slot form of Function is not a scoping construct as there is no known way to nest one such function inside another with access to the parameters of both functions at the same level. All solutions to that issue involve named parameters.

Since a scoping construct in the context of variable renaming means a structure that can be nested in a fashion that # & cannot, # & is not a scoping construct.

I acknowledge that this argument may be tautological, but in some way the question itself seems tautological: Scoping constructs rename the parameters if inner scoping constructs; Slot Functions do not rename parameters of inner scoping constructs; ergo Slot Functions do not behave like scoping constructs.

• @AlexeyPopkov It is more like a macro. As Mr.Wizard said, scoping construct introduces local scope, that has its domain of visibility in code. The slot parameters are visible from anywhere, which makes them actually placeholders, and makes # - & style of functions essentially macros. The other side of that is that their nesting capabilities are more limited. In your example, you have 2 functions, and you had to use named parameter syntax for one of them precisely because you can't achieve proper nesting using only slots. Commented Aug 23, 2016 at 13:23
• +1 I'll note in passing that while it is possible to rewrite Function[x, Function[y, x + y]] as #[#2[1] + #3] &[Function, Slot, #] &, anyone who does so should probably be shot... ducks :) Commented Aug 23, 2016 at 14:44
• My example was meant as a curiosity rather than as an argument against the premise that a slotted function is effectively not a scoping construct. My example games the system to avoid lexical nesting of slots which, as Mr.Wizard states, cannot be done. I agree with Mr.Wizard's analysis as well as the points made by @LeonidShifrin. Commented Aug 23, 2016 at 20:12
• @LeonidShifrin My point (may be very silly) is that there are two #: one is bound to the inner Function's first argument (which is verbatim Function) and one is bound to the outer Function's argument (arbitrary), but both are inside of the outer Function. So the # of the inner Function is shielded from the outer. I'm sure I misunderstand something but I feel this as a kind of scoping. Commented Aug 23, 2016 at 20:46
• @AlexeyPopkov Ok, I partly agree. The fact that inner functions protect their slots from being bound in outer functions is indeed a kind of scoping. A 'reduced' one, however - as it does not directly allow for non-trivial nesting. Commented Aug 23, 2016 at 22:39

According to Wikipedia,

The strict definition of the (lexical) "scope" of a name (identifier) is unambiguous – it is "the portion of source code in which a binding of a name with an entity applies" – and is virtually unchanged from its 1960 definition in the specification of ALGOL 60.

The behavior of the #-& construct conforms this definition: the binding of the identifier # applies only inside of the Function body excepting the bodies of enclosed other #-& constructs (which use the same identifier and hence mask it):

#@*(#^2 &) &[Sin]
%[x]

Sin@*(#1^2 &)

Sin[x^2]


In this respect nested #-& constructs behave exactly as nested pure functions with identical parameter names:

Function[x, x@*Function[x, x^2]][Sin]
%[x]

Sin@*Function[x, x^2]

Sin[x^2]


So anonymous pure function should be considered as a reduced version of a pure function with named parameters in the sense that the names of the parameters of the former are fixed and can't be changed.

The fact that anonymous pure functions do not rename variables in nested scoping constructs has no relation to the question of whether anonymous pure functions themselves are scoped or not: it is just a matter of implementation of lexical scoping, unique feature of anonymous pure functions (in the Wolfram Language). The above example clearly shows that anonymous pure function is indeed lexically scoped: the parameters of the inner pure function aren't accessible from the outside of its body, and mask the parameters of the enclosing pure function.