18

Usually h @@@ {f[1, 2], f[3, 4]} === {h[1, 2], h[3, 4]} but this is not the case when f is Complex: h @@@ {1 + 2I, 3 + 4I } === {1 + 2 I, 3 + 4 I} Since Complexis an atomic and as documentation for Apply states: Applying to atomic objects that do not have subparts effectively does nothing

Using Block to replace Complex with complex gives result as expected for non-atomic case:

Block[{Complex = complex},
 List @@@ {Complex[1, 2], Complex[3, 4]}
 ]
(* {{1, 2}, {3, 4}} *)

But then how come replacing Complex with List while not Apply-ing does not give the same result?

Block[{Complex = List},
 {Complex[1, 2], Complex[3, 4]}
 ]
(* {Complex[1, 2], Complex[3, 4]} *)

As it would have for a non-atomic head:

Block[{f = List},
 {f[1, 2], f[3, 4]}
 ]
(* {{1, 2}, {3, 4}} *)
ssch
  • 16,590
  • 2
  • 53
  • 88
  • 6
    Funnily enough, if I use TracePrint[Block[{Complex = List}, {Complex[1, 2], Complex[3, 4]}]], the replacement happens. Quite odd, this evaluation... – J. M.'s missing motivation Jun 19 '13 at 16:19
  • 1
    With[{Complex = List}, {Complex[1, 2], Complex[3, 4]}] works, but still does not explain the behavior. Block[{Complex = complex}, {Complex[1, 2], Complex[3, 4]}] does not work either, so Applying seems to be the key – Ajasja Jun 19 '13 at 16:32
  • 3
    This is a very strange behavior. For example, this works: Block[{Complex}, Complex := List; {Complex[1, 2], Complex[3, 4]}], and after that, the original example works too. Looks like some changes are not propagated properly. – Leonid Shifrin Jun 19 '13 at 16:33
  • As one might expect, this general technique also doesn't work if the head Complex exists only by implication, as in a packed array. – Oleksandr R. Jun 19 '13 at 16:45
  • @Oleksandr I think that's a given, as atomic objects do not evaluate, and Block only affects things that do; right? – Mr.Wizard Jun 19 '13 at 16:46
  • @Mr.Wizard it may be interesting to compare with Unprotect[Graph]; Graph[{1 \[UndirectedEdge] 2, 2 \[UndirectedEdge] 3}] // AtomQ (*True*); Graph = hh; Graph[{1 \[UndirectedEdge] 2, 2 \[UndirectedEdge] 3}]; Quit[] then. Even though Mathematica says the expression is an atom, we do get the substitution of heads in this case. – Jacob Akkerboom Jun 19 '13 at 18:39
  • 1
    Apparently Complex[1, 2] is not equivalent to 1 + 2I. Block[{Complex = f}, List @@@ {Complex[1, 2], 3 + 4 I}] gives {{1, 2}, 3 + 4I} I thought that was just a syntactic difference – ssch Jun 19 '13 at 18:44
  • @JacobAkkerboom Perhaps related to Graph having subparts while Complex does not. (i.e. you can't get the real part of a Complex with (1+2I)[[1]]) – ssch Jun 19 '13 at 18:46
  • @ssch you can also not take the first part of an expression with head Graph. – Jacob Akkerboom Jun 19 '13 at 19:05
  • Mathematica is full of surprises, isn't it! – Simon Woods Jun 19 '13 at 19:59
  • More surprises: Block[{Complex}, Plus[3, Complex[0, 4]]] + 2-> 5 + Complex[0, 4]. Which is crazy. Also, Trace lies about how 3 + 4 I becomes Complex[3,4]. What a mess. – Jacob Akkerboom Jun 19 '13 at 21:50

1 Answers1

2

This is hardly a complete answer but I suspect this is the result of special handling of the symbol Complex, much as there is special handling of packed arrays.

Remember that Block only affects things that evaluate, e.g. Block[{a = 1}, Hold[a, b, c]] returns Hold[a, b, c]. I believe that Complex may be passed over when it comes to evaluation. Consider this example outside of Block:

Unprotect[Complex];
Complex = ff;
Complex[1, 2]
Quit[]
1 + 2 I

The head Complex is never evaluated to ff here. Interestingly, with a delayed definition it is:

Unprotect[Complex];
Complex := ff
Complex[1, 2]
Quit[]
ff[1, 2]

I cannot think of a reason within the normal evaluation process for this to be, hence my suspicion of special handling.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371