# Why is Evaluate not handle on the Association?

This is a demo code

r = {1, 2, 3};

fs1 = Table[
Function[t,
Evaluate@{"arg" -> t, "diff" -> f[r[[i]], t]}],
{i, 3}];

fs2 = Table[
Function[t,
Evaluate@<|"arg" -> t, "diff" -> f[r[[i]], t]|>],
{i, 3}];


fs1[[2]]["hi"] outputs {"arg" -> "hi", "diff" -> f[2, "hi"]}, however fs2[[2]]["hi"] outputs <|"arg" -> t, "diff" -> f[2, t]|>

• Probably related to the fact that an evaluated association is 'constructed' and atomic. See asso = <|"arg" -> t, "diff" -> 2 t|>; asso /. t -> 1 for example. That is not enough to explain why Function[t, Evaluate@<|1 -> t|>]@2 does not work though.
– Kuba
Aug 13, 2021 at 8:51
• Attributes[Association] contains HoldAllComplete. Aug 13, 2021 at 8:52
• @tueda yes but Function[t, Evaluate@HoldComplete[t]]@2 still injects the argument.
– Kuba
Aug 13, 2021 at 8:53
• Aug 14, 2021 at 20:56
• Not a direct answer, but you can use Table[With[{ri = r[[i]]}, Function[t, <|"arg" -> t, "diff" -> f[ri, t]|>]], {i, 3}] to fix this. Aug 16, 2021 at 17:05

The issue is that Association is atomic. Since Association is also HoldAllComplete, let's examine an atomic object that is not HoldAllComplete.

Clear[x]
g = Graph[{x->1}];
g //AtomQ
g //InputForm


True

Graph[{x, 1}, {DirectedEdge[x, 1]}]

Now, give x a value:

x = 2;
g //InputForm


Graph[{x, 1}, {DirectedEdge[x, 1]}]

Notice that x has not changed inside of the Graph object. This is exactly what is happening in your example.

• I agree with the diagnosis but SparseArray is atomic as well yet: Function[t, Evaluate@SparseArray[{i_, i_} -> t, 3]][2] // InputForm.
– Kuba
Aug 17, 2021 at 6:09
• To me it is a confusing design to have atomic "objects" which do not look like there are atomic (in the sense that they have no subparts). Aug 17, 2021 at 7:55

It must be clearly a bug, because (ToExpression@ToString@fs2[[2]])[hi] works correctly. Moreover,

On[Assert];
Assert[fs2[[2]] == Function[t, <|"arg" -> t, "diff" -> f[2, t]|>]]


gives:

Assert::asrttf: Assertion test fs2[[2]]==Function[t,Association[arg->t,diff->f[2,t]]] evaluated to Function[t,<|arg->t,diff->f[2,t]|>]==Function[t,Association[arg->t,diff->f[2,t]]]
that is neither True nor False.


while Assert[fs1[[2]] == Function[t, {"arg" -> t, "diff" -> f[2, t]}]] succeedes.

Also it is definitely Not an issue with atomicity:

fs1 = Table[Function[t, {"arg" -> t, "diff" -> f[r[[i]], t]}], {i, 1, 3}]
fs2 = Table[Function[t, <|"arg" -> t, "diff" -> f[r[[i]], t]|>], {i, 1, 3}]

fs1[[2]][hi]
Part::pkspec1: The expression i cannot be used as a part specification.
{"arg" -> hi, "diff" -> f[{1, 2, 3}[[i]], hi]}

fs2[[2]][hi]
Part::pkspec1: The expression i cannot be used as a part specification.
<|"arg" -> hi, "diff" -> f[{1, 2, 3}[[i]], hi]|>

fs1[[2]] // AtomQ
False
fs2[[2]] // AtomQ
False


w/o Evaluate or if you replace r[[i]] with r[[2]] both fs1 and fs2 succeede to replace t with hi.

• With ToString/ToExpression the association is destroyed and constructed again and with the latter stage it can have values injected inside.
– Kuba
Aug 17, 2021 at 6:07
• @Kuba no, it is not injected, because if you just type the output of Evaluate by hand: fs3 ={Function[t,<|"arg"->t,"diff"->f[1,t]|>],Function[t,<|"arg"->t,"diff"->f[2,t]|>],Function[t,<|"arg"->t,"diff"->f[3,t]|>]} and then call it fs3[[2]][hi] you will get the correct return value <|arg->hi,diff->f[2,hi]|> . So it's obviously someting wrong is going on inside the VM of WL or whatever they call it. Aug 17, 2021 at 6:44
• Your 'by hand' example, without Evaluate has associations before construction, and they are not constructed before function injects t because Fuction is HoldAll. Then they are constructed and atomic but t is already replaced. This is confusing because constructed and not associations look the same as explained in a topic linked in comments. What I meant earlier is that associations constructed (forced by Evaluate) will be destroyed with ToString and ToExpression will create a function again, but without Evaluate so it will indeed 'work'
– Kuba
Aug 17, 2021 at 7:01