# 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. May 21, 2017 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. May 21, 2017 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. May 21, 2017 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? May 21, 2017 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.

This could cause real trouble. It would be good to have a way to remove the memoization. The following will work for this particular example.

SubValues @ GeneralUtilitiesSlice =
DeleteCases[SubValues @ GeneralUtilities`Slice, 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.