I would not classify this as a bug. The behaviour of a Slot
expression that is not directly contained within a Function
expression is not defined by the documentation, and is unreliable in practice. Consider the following two functions:
f[x_] := x + 100
g[x_] := x + # &[100]
They appear to be essentially equivalent, but it just so happens that the implementation of the second function involves a pure function. However, they return different results when passed a "dangling" slot reference (i.e. a slot reference that is not directly contained within a pure function):
f[#]
(* 100 + #1 *)
g[#]
(* 200 *)
The second result might surprise us, especially if the documentation for g
said "adds 100 to its argument". Consider what happens if we call g
with the Module
expression from the question:
g[Module[{slot = 1}, Slot[slot]]]
(* 200 *)
200
is perhaps not exactly what we expected, but at least it is consistent with g[#]
.
On the other hand, let's try it with a new function h
which is the same as g
but holds its argument:
SetAttributes[h, HoldAll]
h[x_] := x + # &[100]
h[Module[{slot = 1}, Slot[slot]]]
(*
Function::slot: Slot[slot] (in Module[{slot=1},Slot[slot]]+#1&) should contain a non-negative integer. >>
(Module[{slot=1},Slot[slot]]+#1&)[100]
*)
We get the same warning message as in the question. This occurs because this expression ultimately resolves to:
Module[{slot = 1}, Slot[slot]] &
... which is manifestly incorrect since arguments to Slot
in a function body must be non-negative integers -- not symbols.
ParallelEvaluate
happens to be implemented somewhat like h
. It holds the argument, and its implementation happens to use a pure function.
The moral of this story is that the behaviour of a dangling slot reference is essentially undefined. Its behaviour depends upon the exact implementation of the functions to which it is passed. If we are unaware of those exact implementation details, then we should avoid the use of dangling slot references since we can never be sure if that slot expression might not accidentally find its way into an internal Function
expression.
To work around this difficulty, we can use a temporary symbol in place of Slot
and then substitute it out after the evaluations are complete:
Module[{t}
, ParallelEvaluate[Module[{slot = 1}, t[slot]]] /. t -> Slot
]
This behaviour is just another example of the kind of "gotchas" that occur due to Mathematica's simulation of functional programming constructs through pattern matching techniques.
Module
withWith
$\endgroup$Function
:Function@Module[{s = 1}, Slot[s]]
. Perhaps constructing a function is part of the parallel distribution/retrieval process. $\endgroup$