Bags and non-standard evaluation

What can internally be happening here? Is the evaluator just messing with us and going non-standard because it's a Bag? Or am I just not seeing how something like this could be done in Mathematica?

AppendTo[\$ContextPath, "Internal"];

In[19]:= ClearAll[x, y];
{x, y} = {Bag[{1, 2, 3}], Bag[{4, 5}]};


Heads are

In[21]:= Head /@ {x, y}

Out[21]= {Bag, Bag}


But they are just a head, no depth. Depths

In[22]:= Depth /@ {x, y}

Out[22]= {1, 1}


However, they are different. I take their second elements, or I print them in InputForm

In[23]:= BagPart[#, 2] & /@ {x, y}
InputForm /@ {x, y}

Out[23]= {2, 5}

Out[24]= {InputForm[InternalBag[{1, 2, 3}]], InputForm[InternalBag[{4, 5}]]}

-

2 Answers

This is not related to evaluation. InternalBag, like many other special types, is an atomic object. You can verify this using AtomQ[x]. This is despite its InputForm suggesting a structure.

This is no different from how Graph, Rational, Complex or SparseArray behave. (Though for SparseArray, most list manipulation functions are implemented, so it's much more difficult to notice that it is atomic).

A similar example using Rational:

In[206]:= rats={1/2,2/3}
Out[206]= {1/2,2/3}

In[208]:= Head/@rats
Out[208]= {Rational,Rational}

In[209]:= Depth/@rats
Out[209]= {1,1}

In[210]:= FullForm[rats]
Out[210]//FullForm= List[Rational[1,2],Rational[2,3]]

-
I am starting to hate to vote for your answers because I will never catch up, but their quality compels me. +1 yet again – Mr.Wizard Jan 28 '12 at 17:11
I would make a distinction between Graphs and other objects. For most atomic objects in the past, it was possible to deconstruct and reconstruct them by using their InputForm (FullForm), the feature which I consider essential. This plays well with immutability. For Graph-s, a decision was made to introduce mutable state (properties), and their InputForm is deceiving. I dislike this feature, and also think that one should distinguish these two situations. You are certainly well-aware of this discussion: stackoverflow.com/questions/5964469/… – Leonid Shifrin Jan 28 '12 at 17:13
Actually, I gave the wrong link. This is the one I meant: stackoverflow.com/questions/4301833/…, and also I share the point of @WReach which he expressed here: stackoverflow.com/questions/7363253/… – Leonid Shifrin Jan 28 '12 at 17:25

I think InternalBag is a monolithic object which is only constructed using the Bag[list] syntax. Note that the same is true for Graph:

Graph[{1<->2,2<->3}]//Head
(*
==> Graph
*)
Graph[{1<->2,2<->3}]//Depth
(*
==> 1
*)


Also note the output of the objects when typing directly in the kernel:

InternalBag[{2,3}]
(*
==> InternalBag[<2>]
*)
Graph[{1<->2,2<->3}]
(*
==> Graph[<3>, <2>]
*)


Even FullForm doesn't give more information, which shows that on the Mathematica expression level that's all there is.

-
It is not that surprising that FullForm is broken with Bag since Bag is not really meant for user consumption.. – Szabolcs Jan 28 '12 at 17:12
@Szabolcs: But Graph is definitely meant for user consumption, and it shows exactly the same behaviour. – celtschk Jan 28 '12 at 17:15
Regarding Graphs, see my comment below @Szabolcs's answer. – Leonid Shifrin Jan 28 '12 at 17:18
Of course, I just meant that the edges are not as polished as for the other types, e.g. FullForm on Graph does work even though it's atomic (but pattern matching doesn't --- while it works for older types like Rational) – Szabolcs Jan 28 '12 at 17:21
Maybe it's a version thing; as I've shown in my post, Graph for me shows the exact same behaviour for FullForm as Bag (parameters showing up as <n>`). – celtschk Jan 28 '12 at 17:25