4
$\begingroup$

I think this may overlap with existing questions, including one I asked years ago, but I'm not sure how, because the behavior I'm seeing is confusing enough I can't even really describe it except to say that Mathematica appears to be mixing up parameter names with symbol values in ways that seem to be flatly erroneous.

Out of an abundance of caution, I'm going to start here:

ClearAll["Global`*"]

Start with a use of select that does something pretty self-explanatory:

Select[{2, 3, a, b, 6, 7, c}, MemberQ[{a, b, c}, #] &]
(* {a, b, c} *)

Now, say we want to make this an anonymous function itself, abstracting over the first argument to MemberQ. One way that works is this:

Select[{2, 3, a, b, 6, 7, c}, Function[x, MemberQ[#, x]]] &@{a, b, c}
(* {a, b, c} *)

Now let's change the name of that formal parameter to Function:

Select[{2, 3, a, b, 6, 7, c}, Function[c, MemberQ[#, c]]] &@{a, b, c}
(* {2, 3, a, b, 6, 7, c} *)

Huh? Looking at a Trace with a minimal argument to Select sort of shows what's going on:

Trace[
 Select[{2, 3, a, b, 6, 7, c}, Function[c, MemberQ[#, c]]] &@{c}]
(*
 {(Select[{1},Function[c,MemberQ[#1,c]]]&)[{c}],
   Select[{1},Function[c,MemberQ[{c},c]]],
   {Function[c, MemberQ[{c},c]][1], MemberQ[{1},1], True}, {1}}
*)

I'm not sure what word to use to describe rewriting

Select[{1},Function[c,MemberQ[#1,c]]]&)[{c}]

as

Select[{1},Function[c,MemberQ[{c},c]]]

but "astonishing" is near the top of the list. It's neither lexical nor dynamic scoping. Nor does setting "StrictLexicalScoping" -> True have any impact.

I hesitate to call this a bug, but it is weird and puzzling.

$\endgroup$
4
  • $\begingroup$ That's not what I get when I trace. I see Function[c, MemberQ[{a, b, c}, c]], and clearly this will always be true. $\endgroup$ – Carl Woll Feb 8 at 18:54
  • 1
    $\begingroup$ That was a copy'n'paste error on my party. Fixed in edit. $\endgroup$ – Pillsy Feb 8 at 19:20
  • 3
    $\begingroup$ This is not a bug, in WL at least. Functions with # work like macros. Essentially you build the inner function at run-time: Function[c, MemberQ[#, c]] &@{a, b, c} -> Function[c, MemberQ[{a, b, c}, c]], and this resulting one is the one that is used in Select. I think the key to remember about parameter passing in WL is that it works like macro-substitution, especially for functions with slots, or rules. Note that named arg Functions are more careful: Function[lst, Function[c, MemberQ[lst, c]]][{a, b, c}] -> Function[c$, MemberQ[{a, b, c}, c$]], which is the result you want. $\endgroup$ – Leonid Shifrin Feb 8 at 19:35
  • 3
    $\begingroup$ Perhaps one can summarize this by saying that that slot-based Functions are macros rather than (lexical) scoping constructs. $\endgroup$ – Leonid Shifrin Feb 8 at 19:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.