# Unexpected behavior of PreIncrement when querying associations

For reasons I won't go into here, I became curious about the what would happen should I make a query that applies PreIncrement to one key in a list of associations. I admit that it could be argued that PreIncrement should not be used in this way. In fact, because PreIncrement is HoldFirst and expects a symbol bound to a value, I was rather expecting Mathematica to complain about what I was about to do. I couldn't see how Mathematica was going to find such a symbol in the situation I was setting up.

The list of associations I will use is

assocs = {<|"a" -> 1, "b" -> 3|>, <|"a" -> 2, "b" -> 2|>, <|"a" -> 3, "b" -> 1|>};


and the query I will make is

Query[All, "b", PreIncrement] @ assocs


The first evaluation.

result = Query[All, "b", PreIncrement] @ assocs; {result, assocs}

{{4, 3, 2},
{<|"a" -> 1, "b" -> 3|>, <|"a" -> 2, "b" -> 2|>, <|"a" -> 3, "b" -> 1|>}}


Surprise. No complaints from Mathematica and the result seems reasonable. Now I'll do it a couple of more times.

result = Query[All, "b", PreIncrement] @ assocs; {result, assocs}

{{5, 4, 3},
{<|"a" -> 1, "b" -> 3|>, <|"a" -> 2, "b" -> 2|>, <|"a" -> 3, "b" -> 1|>}}

result = Query[All, "b", PreIncrement] @ assocs; {result, assocs}

{{6, 5, 4},
{<|"a" -> 1, "b" -> 3|>, <|"a" -> 2, "b" -> 2|>, <|"a" -> 3, "b" -> 1|>}}


This is very strange. I expected result to have the same value after each evaluation. Since none of the associations in assocs is modified by the query, what is PreIncrement actually incrementing?

• For what it's worth ++assocs[[All, "b"]] appears to work correctly. – Mr.Wizard May 21 '17 at 19:11
• @Mr.Wizard. That's a good observation, but everyone should understand that your code in no way involves the Query mechanism. The behavior you report is a simple effect of the associations in assocs being destructively modified, so there is no mystery about where the incremented values are coming from. – m_goldberg May 21 '17 at 19:48
• Right, I just meant that: (1) for some purposes it is a solution (2) the fact that it works as desired but Query does not makes me think that the latter is a bug. – Mr.Wizard May 21 '17 at 19:52
• @Mr.Wizard. I have updated the question's title to make clear that Query is a big part of the issue. As to Slice not being in a state of grace, I feel nervous about the way it's implemented. 1) Should it memoize? This thread seems to show that doing so can be dangerous (at least to anybody expecting functional behavior). 2) Can a function that depends on another function called InternalUnsafeQuietCheckever be fully trusted? – m_goldberg May 21 '17 at 20:08

Before being applied, Query expressions are "compiled" into ordinary compositions of ordinary Wolfram Language functions and their operator forms. To see the compiled form of a Query, use Normal.

That seems like a good line of exploration. Let's see where it leads.

Query[All, "b", PreIncrement] // Normal

Map[GeneralUtilitiesSlice["b"] /* PreIncrement]


And, yes, that function can be used in place of the query I've been using.

Map[GeneralUtilitiesSlice["b"] /* PreIncrement] @ assocs


{7, 6, 5}

?? GeneralUtilitiesSlice

Satori. GeneralUtilitiesSlice is doing memoization. The queries do not extract values from assocs except when the first evaluation of Query is done.
SubValues @ GeneralUtilitiesSlice =
DeleteCases[SubValues @ GeneralUtilitiesSlice, RuleDelayed[_, _Integer]]

The behavior reported in the question also applies to Increment, Decrement, PreDecrement` and to the use of all such operators with data sets.