I want to wrap some association data into a dedicated symbol (S
by example)
S /: S[a_Association][k_] := a[k]
S /: Format[s_S] :=
"Demo, here are my keys " <> ToString[Keys[First[s]]]
I use such approach (but AFAIK this is a common one) as I see two benefits:
1/ easy argument filtering
foo[S[a_Association]]:="Hello S!"
2/ syntaxic & looks sugar:0
s = S[<|"A" -> 1, "B" -> 2|>]
s["A"]
prints:
"Demo, here are my keys {A, B}" 1
However when you have a nice syntax for s["A"]
, you also want a nice syntax for s["A"]=3
. This is even mandatory, as
s["A"]=3
is misleading/bug prone:
It does not change the internal association:
s // First
<|"A" -> 1, "B" -> 2|>
but only binds 3 to s["A"]
:
s["A"]
3
So far my solution to the problem is to modify the buildin Set
function as follows:
Unprotect[Set]
Set[s_Symbol[k_], v_] /; MatchQ[Hold[s] /. OwnValues[s], Hold[_S]] :=
Block[{a = First[s]}, a[k] = v; Set[s, S[a]]]
Protect[Set]
Now
s["A"]=3
s["C"]=4
s // First
prints
"Demo, here are my keys {A, B}" "Demo, here are my keys {A, B, C}" <|"A" -> 3, "B" -> 2, "C" -> 4|>
as expected.
My question:
Before using this in "real" developments, I would like your advices:
is this approach ok (no side effect,...) ?
is there a better approach/solution ?
Set
like this is really a last resort and is best avoided. There are alternative ways (@Kuba provided references to such). $\endgroup$Set
overloading here, so I refer you to that discussion for arguments against it. Also check out this question. None of those discussions will likely have a direct answer to your question, but the references provided by @Kuba should point you to the better direction. $\endgroup$